オブジェクトを初期化するときの{0}の意味は?


252

ときに{0}オブジェクトを初期化するために使用され、それが何を意味するのでしょうか?私は{0}どこへの参照も見つけることができません。中括弧があるため、Google検索は役に立ちません。

コード例:

SHELLEXECUTEINFO sexi = {0}; // what does this do?
sexi.cbSize = sizeof(SHELLEXECUTEINFO);
sexi.hwnd = NULL;
sexi.fMask = SEE_MASK_NOCLOSEPROCESS;
sexi.lpFile = lpFile.c_str();
sexi.lpParameters = args;
sexi.nShow = nShow;

if(ShellExecuteEx(&sexi))
{
    DWORD wait = WaitForSingleObject(sexi.hProcess, INFINITE);
    if(wait == WAIT_OBJECT_0)
        GetExitCodeProcess(sexi.hProcess, &returnCode);
}

これがないと、上記のコードは実行時にクラッシュします。

回答:


302

ここで起こっていることは、集約初期化と呼ばれます。これは、ISO仕様のセクション8.5.1からの集約の(省略された)定義です。

集計とは、ユーザーが宣言したコンストラクター、プライベートまたは保護された非静的データメンバー、基本クラス、仮想関数のない配列またはクラスです。

今、{0}このように集合体を初期化するためにを使用することは、基本的0に全体のトリックです。これは、集約初期化使用する場合、すべてのメンバーを指定する必要はなく、仕様では、指定されていないすべてのメンバーをデフォルトで初期化する必要があるためです0。つまり、単純型に設定されます。

仕様からの引用は次のとおりです。

集合内のメンバーよりもリスト内の初期化子の数が少ない場合、明示的に初期化されていない各メンバーはデフォルトで初期化されます。例:

struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };

ss.awith 1ss.bwith "asdf"、およびss.cの形式の式の値int()、つまりで 初期化し0ます。

このトピックの完全な仕様はここにあります


15
優れた反応。集計を{0}で初期化することは、単に{}で初期化することと同じであることを追加したかっただけです。おそらく、前者は組み込み型がゼロになることをより明白にします。
ジェームズホプキン、

7
一部のコンパイラは{}を
制限し

10
不正解です。C++では、最初のメンバーをゼロ構築できない場合、{0}は機能しません。例:struct A {B b; int i; char c; }; struct B {B(); B(文字列); }; A a = {}; //このステートメントは 'A a = {0}'として書き換えることはできません。
アーロン

25
@Branan、これはCでは「{}」が無効だからです。C ++ではそうです。@ don.neufeld、これはC ++ 03で変更されました(default-initialized-> value-initialized)。引用は、C ++ 98標準を引用しています。
ヨハネスシャウブ-litb 2009

3
それで、これはZeroMemory()(VC ++)を使用する必要性を置き換えるのですか?
Ray

89

注意すべきことの1つは、この手法ではパディングバイトがゼロに設定されないことです。例えば:

struct foo
{
    char c;
    int  i;
};

foo a = {0};

以下と同じではありません:

foo a;
memset(&a,0,sizeof(a));

最初のケースでは、cとiの間の埋め込みバイトは初期化されていません。なぜあなたは気にしますか?このデータをディスクに保存したり、ネットワーク経由で送信したりする場合、セキュリティ上の問題が発生する可能性があります。


13
もちろん、 'write(f、&a、sizeof(a))'を実行した場合のセキュリティ上の問題のみです。これにより、異なるプロセッサ/コンパイラで異なるファイルエンコーディングが生成される可能性があります。適切にフォーマットされた出力は、memsetがなくても安全です。
アーロン

3
また、ネットワークを介してデータを送信する場合は、常に配置がパックされるように設定します。そうすることで、可能な限り少ない追加のパディングバイトを取得できます。
Mark Kegel、

18
仕様ではパディングを初期化する必要はありませんが、「適切な」初期化に時間を費やすだけなので、適切なコンパイラを使用する必要があることに注意してください。
トーマス

4
「ではない」という言葉の使用に問題があります。パディングバイトは実装定義です。コンパイラーは自由にfoo a = {0}をmemset(&a、0、sizeof(a))に自由に変換できます。パディングバイトを「スキップ」する必要はなく、foo.cとfoo.i のみを設定します。(潜在的な)セキュリティバグの+1
SecurityMatt

3
@Leushenkoのポイント19は「明示的に初期化されていないすべてのサブオブジェクト」と言っているので、ポイント21(あなたが引用したもの)はだらしません。特に、指定された初期化子が使用されている場合に初期化子が順不同で表示される可能性があることを考慮すると、仕様によって、最後の初期化子の瞬間までパディングを初期化できず、その後パディングをゼロにする必要があった場合は、本当に奇妙です。
MM

20

空の集約初期化子も機能することに注意してください。

SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};

11

ShellExecuteEx()クラッシュする理由への答えとして、SHELLEXECUTEINFO「sexi」構造体には多くのメンバーがあり、初期化しているのはそれらの一部のみです。

たとえば、メンバーsexi.lpDirectoryがどこを指していてもShellExecuteEx()、それを使用しようとする可能性があるため、メモリアクセス違反が発生します。

次の行を含めると:

SHELLEXECUTEINFO sexi = {0};

構造体のセットアップの残りの部分の前に、関心のある特定のメンバーを初期化する前に、すべての構造体メンバーをゼロにするようコンパイラーに指示しています。ゼロのShellExecuteEx()場合sexi.lpDirectoryは無視する必要があることを知っています。


7

文字列の初期化などにも使用します。

char mytext[100] = {0};

5
もちろん、mytextが文字列として使用されている場合、char mytext [100]; mytext [0] = '\ 0'; 空の文字列を与えるのと同じ効果がありますが、実装は最初のバイトをゼロにします。
クリスヤング

@Chris:部分的なオブジェクト初期化のための構文があることを私はしばしば望んでいました。xを「宣言して初期化」し、次にy、zで同様に実行できることは、x、y、zを宣言してからx、y、zを初期化する必要があるよりもはるかに優れていますが、初期化が実際に必要なのは、かなり無駄が多いようです。
スーパーキャット2017

7

{0}CおよびC ++の両方で、任意の(完全なオブジェクト)型の有効な初期化子です。これは、オブジェクトをゼロに初期化するために使用される一般的なイディオムです(それが何を意味するかを確認するために読んでください)。

スカラー型(算術型およびポインター型)の場合、中括弧は不要ですが、明示的に許可されます。ISO C規格のN1570ドラフト、セクション6.7.9を引用:

スカラーの初期化子は、オプションで中括弧で囲まれた単一の式でなければなりません。

オブジェクトをゼロに初期化します(0整数の場合0.0、浮動小数点の場合、ポインターのnullポインター)。

非スカラー型(構造体、配列、共用体)の場合、オブジェクトの最初の要素がゼロに初期化{0}されることを指定します。構造、構造の配列などを含む構造の場合、これは再帰的に適用されるため、最初のスカラー要素は、型に応じてゼロに設定されます。他の初期化子と同様に、指定されていない要素はすべてゼロに設定されます。

中括弧({})は省略できます。たとえば、これらは両方とも有効で同等です。

int arr[2][2] = { { 1, 2 }, {3, 4} };

int arr[2][2] = { 1, 2, 3, 4 };

そのため、たとえば、{ { 0 } }最初の要素が非スカラーである型を記述する必要はありません。

したがって、この:

some_type obj = { 0 };

初期化の簡略な方法でobj、ゼロに意味をその各スカラーサブオブジェクトobjに設定され0、それは整数だ場合、0.0それはポインタだ場合には、またはNULLポインタ浮動小数点かどう。

ルールはC ++でも同様です。

あなたの特定のケースでは、値を割り当てるsexi.cbSizeなどしているので、それSHELLEXECUTEINFOが構造体型またはクラス型(またはおそらくユニオンかもしれませんが、おそらくそうではない)であることが明らかなので、これのすべてが当てはまるわけではありません{ 0 }が、より一般的な状況で使用できるイディオム。

これは(必然的に)を使用memsetしてオブジェクトの表現をすべてビット0に設定することとは異なります。浮動小数点0.0もnullポインターも必ずしもすべてがゼロのビットとして表される{ 0 }わけではなく、初期化子はパディングバイトを特定の値に設定する必要はありません。ただし、ほとんどのシステムでは、同じ効果が得られる可能性があります。


1
C ++では、{0}はコンストラクタを受け入れないオブジェクトの有効な初期化子ではありません0。また、最初の要素がそのようなものである集約(または要素のない集約)の場合
MM

3

私がc / c ++で作業してからしばらく経ちますが、IIRCでは、配列にも同じショートカットを使用できます。


2

私はいつも疑問に思っていました、なぜあなたは何かを使うべきなのか

struct foo bar = { 0 };

これは説明するためのテストケースです:

check.c

struct f {
    int x;
    char a;
} my_zero_struct;

int main(void)
{
    return my_zero_struct.x;
}

でコンパイルしてgcc -O2 -o check check.cからシンボルテーブルを出力しますreadelf -s check | sort -k 2(これは、x64システムのubuntu 12.04.2のgcc 4.6.3を使用しています)。抜粋:

59: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
48: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
25: 0000000000601018     0 SECTION LOCAL  DEFAULT   25 
33: 0000000000601018     1 OBJECT  LOCAL  DEFAULT   25 completed.6531
34: 0000000000601020     8 OBJECT  LOCAL  DEFAULT   25 dtor_idx.6533
62: 0000000000601028     8 OBJECT  GLOBAL DEFAULT   25 my_zero_struct
57: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT  ABS _end

ここで重要なのは、のmy_zero_struct__bss_startです。Cプログラムの「.bss」セクションは、.bssのウィキペディアを参照する にゼロ設定されているメモリのセクションです。main

上記のコードを次のように変更した場合:

} my_zero_struct = { 0 };

次に、結果の「チェック」実行可能ファイルは、ubuntu 12.04.2上のgcc 4.6.3コンパイラでは少なくともまったく同じに見えます。これmy_zero_structはまだ.bssセクションにあるため、mainが呼び出される前に自動的にゼロに初期化されます。

memset「フル」構造が初期化される可能性があるというコメントのヒントも、.bssセクションが完全にクリアされるため「改善」ではありません。これは、「フル」構造がゼロに設定されることも意味します。

C言語標準ではこれについて何も言及されていない可能性ありますが、実際のCコンパイラでは、これまでとは異なる動作を見たことがありません。


グローバル変数と静的変数は、常にデフォルトで0またはデフォルトのctorに初期化されます。しかし、fのインスタンスをローカルで宣言すると、異なる結果が得られる場合があります。
Logman

0

これは、構造全体を空/ゼロ/ null値に初期化するための構文上の砂糖です。

ロングバージョン

SHELLEXECUTEINFO sexi;
sexi.cbSize = 0;
sexi.fMask = 0;
sexi.hwnd = NULL;
sexi.lpVerb = NULL;
sexi.lpFile = NULL;
sexi.lpParameters = NULL;
sexi.lpDirectory = NULL;
sexi.nShow = nShow;
sexi.hInstApp = 0;
sexi.lpIDList = NULL;
sexi.lpClass = NULL;
sexi.hkeyClass = 0;
sexi.dwHotKey = 0;
sexi.hMonitor = 0;
sexi.hProcess = 0;

短縮版

SHELLEXECUTEINFO sexi = {0};

それほど簡単ではありませんでしたか?

それはまた、次の理由から素晴らしいです。

  • すべてのメンバーを探し出して初期化する必要はありません
  • 新しいメンバーが後で追加されたときに初期化しない可能性があることを心配する必要はありません
  • あなたは電話する必要はありませんZeroMemory

-5

{0}は、その要素を0として含む無名配列です。

これは、配列の1つまたはすべての要素を0で初期化するために使用されます。

例:int arr [8] = {0};

この場合、arrのすべての要素は0として初期化されます。


4
{0}は無名配列ではありません。それも表現ではありません。イニシャライザです。
キーストンプソン、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.