web-dev-qa-db-ja.com

構造体をゼロにするmemset()または値の初期化?

Win32 APIプログラミングでは、複数のフィールドでC structsを使用するのが一般的です。通常、意味のある値を持っているのはそのうちの2つだけで、他のすべてはゼロにする必要があります。これは、次の2つの方法のいずれかで実現できます。

STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );

または

STRUCT theStruct = {};

2番目のバリアントはよりきれいに見えます-ワンライナーで、タイプミスがあり、エラーが発生する可能性のあるパラメーターはありません。

最初のバリアントと比較して欠点はありますか?使用するバリアントとその理由

61
sharptooth

これらの2つの構成は、意味が異なるveryを構成します。最初のものはmemset関数を使用します。これはメモリのバッファを特定の値に設定するを目的としています。 2番目のオブジェクトの初期化。少しのコードで説明しましょう:

メンバーを持つ構造があると仮定しましょうPODタイプのみ

_struct POD_OnlyStruct
{
    int a;
    char b;
};

POD_OnlyStruct t = {};  // OK

POD_OnlyStruct t;
memset(&t, 0, sizeof t);  // OK as well
_

この場合、_POD_OnlyStruct t = {}_またはPOD_OnlyStruct t; memset(&t, 0, sizeof t)を書いてもそれほど違いはありません。ここにある唯一の違いはalignmentバイトが0に設定されているからです。 memsetのケースが使用されます。通常はこれらのバイトにアクセスできないため、違いはありません。

一方、質問にC++のタグを付けたので、メンバーPODとは異なるタイプの別の例を試してみましょう。

_struct TestStruct
{
    int a;
    std::string b;
};

TestStruct t = {};  // OK

{
    TestStruct t1;
    memset(&t1, 0, sizeof t1);  // ruins member 'b' of our struct
}  // Application crashes here
_

この場合、_TestStruct t = {}_のような式を使用するのが適切であり、memsetを使用するとクラッシュにつながります。 memsetを使用すると、次のようになります。タイプTestStructのオブジェクトが作成され、構造体のメンバーであるため、タイプ_std::string_のオブジェクトが作成されます。次に、memsetは、オブジェクトbが配置されたメモリを特定の値、たとえばゼロに設定します。これで、TestStructオブジェクトがスコープ外に出ると破棄され、そのメンバーの_std::string b_になったときにクラッシュが発生します。そのオブジェクトの内部構造はすべてmemsetによって破壊されたためです。

つまり、現実はこれらは非常に異なるであり、特定の場合にmemset構造全体をゼロにする必要がある場合がありますが、何をしているかを理解することは常に重要です。 2番目の例のように間違えないでください。

私の投票-オブジェクトでmemsetを使用してくださいのみ必要な場合、およびdefault初期化_x = {}_を他のすべての場合に使用してください。

84
Dmitry

構造体のメンバーによっては、2つのバリアントは必ずしも同等ではありません。 memsetは構造体をall-bits-zeroに設定しますが、値の初期化はすべてのメンバーを値0に初期化します。 C標準では、これらが整数型に対してのみ同じであることが保証されており、浮動小数点値またはポインターに対しては同じではありません。

また、一部のAPIでは、構造を実際にall-bits-zeroに設定する必要があります。たとえば、BerkeleyソケットAPIは構造を多相的に使用するため、明らかな値だけでなく、構造全体を実際にゼロに設定することが重要です。 APIのドキュメントには、構造を実際にすべてビットゼロにする必要があるかどうかが記載されている必要がありますが、不足している可能性があります。

しかし、これらのいずれか、または同様のケースが当てはまらない場合、それはあなた次第です。構造を定義するときは、値の初期化を優先します。これは、意図をより明確に伝えるためです。もちろん、既存の構造をゼロ化する必要がある場合は、memsetが唯一の選択肢です(まあ、各メンバーを手動でゼロに初期化することは別ですが、特に大きな構造の場合は通常行われません)。

29
JaakkoK

構造体に次のようなものが含まれている場合:

int a;
char b;
int c;

次に、「b」と「c」の間にパディングのバイトが挿入されます。 memset()はそれらをゼロにしますが、他の方法はそうしないので、3バイトのゴミがあります(intが32ビットの場合)。構造体を使用してファイルの読み取り/書き込みを行う場合、これは重要です。

10
peufeu

あなたが言及したように、それはきれいに見え、エラーが発生しにくいため、値の初期化を使用します。私はそれをすることにどんな欠点も見ません。

ただし、memsetを使用して、構造体を使用した後に構造体をゼロにすることができます。

7
Gregory Pakosz

一般的ではありませんが、2番目の方法には、floatをゼロに初期化する利点もあると思います。 memsetを実行すると、確かに

5
Toad

一部のコンパイラでは、_STRUCT theStruct = {};_は実行可能ファイルでmemset( &theStruct, 0, sizeof( STRUCT ) );に変換されます。一部のC関数は、ランタイムセットアップを行うために既にリンクされているため、コンパイラはmemset/memcpyなどのこれらのライブラリ関数を使用できます。

3
Gerhard

コンパイル時に実行できるため、値の初期化。
また、すべてのPODタイプを正しく0に初期化します。

Memset()は実行時に行われます。
また、構造体がPODでない場合、memset()の使用は疑わしいです。
非int型を正しく(ゼロに)初期化しません。

2
Martin York