it-swarm-eu.dev

Was bedeutet {0} beim Initialisieren eines Objekts?

Was bedeutet es, wenn mit {0} Ein Objekt initialisiert wird? Ich kann nirgendwo Verweise auf {0} Finden und die Google-Suche ist aufgrund der geschweiften Klammern nicht hilfreich.

Beispielcode:

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);
}

Andernfalls stürzt der obige Code zur Laufzeit ab.

249

Was hier passiert, wird als Aggregatinitialisierung bezeichnet. Hier ist die (abgekürzte) Definition eines Aggregats aus Abschnitt 8.5.1 der ISO-Spezifikation:

Ein Aggregat ist ein Array oder eine Klasse ohne vom Benutzer deklarierte Konstruktoren, ohne private oder geschützte nicht statische Datenelemente, ohne Basisklassen und ohne virtuelle Funktionen.

Die Initialisierung eines solchen Aggregats mit {0} Ist im Grunde genommen ein Trick, um das Ganze mit 0 Zu initialisieren. Dies liegt daran, dass bei der Verwendung der Aggregatinitialisierung nicht alle Elemente angegeben werden müssen und die Spezifikation erfordert, dass alle nicht angegebenen Elemente standardmäßig initialisiert werden, dh festgelegt werden für einfache Typen auf 0.

Hier ist das relevante Zitat aus der Spezifikation:

Befinden sich weniger Initialisierer in der Liste als Mitglieder im Aggregat, wird jedes nicht explizit initialisierte Mitglied standardmäßig initialisiert. Beispiel:

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

initialisiert ss.a mit 1, ss.b mit "asdf" und ss.c mit dem Wert eines Ausdrucks der Form int(), also 0.

Die vollständige Spezifikation zu diesem Thema finden Sie hier

298
Don Neufeld

Beachten Sie, dass bei dieser Technik die Auffüllbytes nicht auf Null gesetzt werden. Beispielsweise:

struct foo
{
    char c;
    int  i;
};

foo a = {0};

Ist nicht dasselbe wie:

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

Im ersten Fall werden Pad-Bytes zwischen c und i nicht initialisiert. Warum sollte es dich interessieren? Wenn Sie diese Daten auf einem Datenträger speichern oder über ein Netzwerk senden oder was auch immer, könnte dies ein Sicherheitsproblem darstellen.

89
Harold Ekstrom

Beachten Sie, dass ein leerer Aggregatinitialisierer auch funktioniert:

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

Als Antwort darauf, warum ShellExecuteEx() abstürzt: Ihre SHELLEXECUTEINFO "sexi" -Struktur hat viele Mitglieder und Sie initialisieren nur einige von ihnen.

Zum Beispiel könnte der Member sexi.lpDirectory Auf eine beliebige Stelle zeigen, aber ShellExecuteEx() wird immer noch versuchen, ihn zu verwenden, daher wird eine Speicherzugriffsverletzung angezeigt.

Wenn Sie die Zeile einfügen:

SHELLEXECUTEINFO sexi = {0};

vor dem Rest der Strukturkonfiguration weisen Sie den Compiler an, all Strukturelemente auf Null zu setzen, bevor Sie die spezifischen Elemente initialisieren, an denen Sie interessiert sind. ShellExecuteEx() weiß, dass wenn sexi.lpDirectory Ist Null und sollte ignoriert werden.

11
snowcrash09

Ich benutze es auch, um Strings zu initialisieren, zB.

char mytext[100] = {0};
7
Adam Pierce

{0} Ist ein gültiger Initialisierer für jeden (vollständigen Objekt-) Typ in C und C++. Es ist eine gebräuchliche Redewendung, die verwendet wird, um ein Objekt auf Null zu initialisieren (lesen Sie weiter, um zu sehen, was dies bedeutet).

Für skalare Typen (arithmetische Typen und Zeigertypen) sind die geschweiften Klammern nicht erforderlich, jedoch ausdrücklich zulässig. Zitieren des N1570-Entwurf der ISO C-Norm, Abschnitt 6.7.9:

Der Initialisierer für einen Skalar muss ein einzelner Ausdruck sein, der optional in geschweiften Klammern eingeschlossen ist.

Es initialisiert das Objekt mit Null (0 Für Ganzzahlen, 0.0 Für Gleitkommazahlen, ein Nullzeiger für Zeiger).

Bei nicht skalaren Typen (Strukturen, Arrays, Vereinigungen) gibt {0} An, dass das erste Element des Objekts mit Null initialisiert wird. Für Strukturen, die Strukturen, Arrays von Strukturen usw. enthalten, wird dies rekursiv angewendet, sodass das erste skalare Element entsprechend dem Typ auf Null gesetzt wird. Wie in jedem Initialisierer werden alle nicht angegebenen Elemente auf Null gesetzt.

Zwischenklammern ({, }) Können weggelassen werden. Zum Beispiel sind beide gültig und äquivalent:

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

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

aus diesem Grund müssen Sie beispielsweise nicht { { 0 } } für einen Typ schreiben, dessen erstes Element nicht skalar ist.

Also das:

some_type obj = { 0 };

ist eine Kurzform zum Initialisieren von obj auf Null, dh, jedes skalare Unterobjekt von obj wird auf 0 gesetzt, wenn es sich um eine Ganzzahl handelt, 0.0 wenn Es ist ein Gleitkomma oder ein Nullzeiger, wenn es ein Zeiger ist.

Die Regeln für C++ sind ähnlich.

Da Sie in Ihrem speziellen Fall sexi.cbSize Usw. Werte zuweisen, ist es klar, dass SHELLEXECUTEINFO ein Struktur- oder Klassentyp ist (oder möglicherweise eine Vereinigung, aber wahrscheinlich nicht), also nicht All dies gilt, aber wie gesagt { 0 } ist eine gebräuchliche Redewendung, die in allgemeineren Situationen verwendet werden kann.

Dies ist nicht (unbedingt) gleichbedeutend mit der Verwendung von memset, um die Darstellung des Objekts auf Null zu setzen. Weder Gleitkomma 0.0 Noch ein Nullzeiger werden notwendigerweise als All-Bit-Null dargestellt, und ein { 0 } - Initialisierer setzt die Füllbytes nicht notwendigerweise auf einen bestimmten Wert. Auf den meisten Systemen hat dies jedoch wahrscheinlich den gleichen Effekt.

7
Keith Thompson

Es ist schon eine Weile her, dass ich in C/C++ gearbeitet habe, aber IIRC, die gleiche Verknüpfung kann auch für Arrays verwendet werden.

3
µBio

Ich habe mich immer gefragt, warum Sie sollten so etwas verwenden

struct foo bar = { 0 };

Hier ist ein Testfall zur Erklärung:

check.c

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

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

Ich kompiliere mit gcc -O2 -o check check.c Und gebe dann die Symboltabelle mit readelf -s check | sort -k 2 Aus (dies ist mit gcc 4.6.3 unter Ubuntu 12.04.2 auf einem x64-System). Auszug:

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

Der wichtige Teil hier ist, dass my_zero_struct Nach __bss_start Steht. Der Abschnitt ".bss" in einem C-Programm ist der Speicherabschnitt, der auf Null gesetzt ist vormain heißt see wikipedia on .bss .

Wenn Sie den obigen Code ändern in:

} my_zero_struct = { 0 };

Dann sieht die resultierende ausführbare Datei "check" wie folgt aus: gena Zumindest mit dem Compiler gcc 4.6.3 auf Ubuntu 12.04.2; Der my_zero_struct befindet sich immer noch im Abschnitt .bss und wird daher automatisch auf Null initialisiert, bevor main aufgerufen wird.

Hinweise in den Kommentaren, dass ein memset die "vollständige" Struktur initialisieren könnte, sind ebenfalls keine Verbesserung, da der Abschnitt .bss Vollständig gelöscht wird, was auch bedeutet, dass die "vollständige" Struktur auf Null gesetzt ist .

Es ist könnte sein, dass der C-Sprachstandard nichts davon erwähnt, aber in einem realen C-Compiler habe ich noch nie ein anderes Verhalten gesehen.

2
Ingo Blackman