Quando {0}
viene utilizzato per inizializzare un oggetto, cosa significa? Non riesco a trovare riferimenti a {0}
ovunque, e a causa delle parentesi graffe le ricerche di Google non sono utili.
Codice di esempio:
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);
}
Senza di esso, il codice sopra si arresterà in modo anomalo in fase di esecuzione.
Quello che sta succedendo qui è chiamato inizializzazione aggregata . Ecco la definizione (abbreviata) di un aggregato dalla sezione 8.5.1 della specifica ISO:
Un aggregato è un array o una classe senza costruttori dichiarati dall'utente, senza membri di dati non statici privati o protetti, senza classi di base e senza funzioni virtuali.
Ora, usare {0}
Per inizializzare un aggregato come questo è fondamentalmente un trucco per 0
L'intera cosa. Questo perché quando si utilizza l'inizializzazione aggregata non è necessario specificare tutti i membri e le specifiche richiedono che tutti i membri non specificati siano inizializzati per impostazione predefinita, il che significa impostare a 0
per i tipi semplici.
Ecco la citazione pertinente dalle specifiche:
Se nell'elenco sono presenti meno inizializzatori rispetto a quelli presenti nell'aggregato, ogni membro non inizializzato esplicitamente deve essere inizializzato per impostazione predefinita. Esempio:
struct S { int a; char* b; int c; }; S ss = { 1, "asdf" };
inizializza
ss.a
con1
,ss.b
con"asdf"
ess.c
con il valore di un'espressione del moduloint()
, ovvero0
.
Puoi trovare le specifiche complete su questo argomento qui
Una cosa da tenere presente è che questa tecnica non imposterà i byte di riempimento a zero. Per esempio:
struct foo
{
char c;
int i;
};
foo a = {0};
Non è lo stesso di:
foo a;
memset(&a,0,sizeof(a));
Nel primo caso, i byte pad tra c e i non sono inizializzati. Perché te ne importa? Bene, se stai salvando questi dati su disco o inviandoli su una rete o altro, potresti avere un problema di sicurezza.
Si noti che funziona anche un inizializzatore di aggregazione vuoto:
SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};
In risposta al motivo per cui ShellExecuteEx()
sta andando in crash: la tua struttura SHELLEXECUTEINFO
"sexi" ha molti membri e ne stai inizializzando solo alcuni.
Ad esempio, il membro sexi.lpDirectory
Potrebbe puntare ovunque, ma ShellExecuteEx()
proverà comunque a usarlo, quindi otterrai una violazione di accesso alla memoria.
Quando includi la linea:
SHELLEXECUTEINFO sexi = {0};
prima del resto della configurazione della struttura, stai dicendo al compilatore di azzerare tutti membri della struttura prima di inizializzare quelli specifici che ti interessano. ShellExecuteEx()
sa che se sexi.lpDirectory
È zero, dovrebbe ignorarlo.
Lo uso anche per inizializzare stringhe ad es.
char mytext[100] = {0};
{0}
è un inizializzatore valido per qualsiasi tipo (oggetto completo), sia in C che in C++. È un linguaggio comune usato per inizializzare un oggetto a zero (continua a leggere per vedere cosa significa).
Per i tipi scalari (tipi aritmetici e puntatori), le parentesi graffe non sono necessarie, ma sono esplicitamente consentite. Citando N1570 draft dello standard ISO C, sezione 6.7.9:
L'inizializzatore per uno scalare deve essere una singola espressione, facoltativamente racchiusa tra parentesi graffe.
Inizializza l'oggetto a zero (0
per numeri interi, 0.0
per virgola mobile, un puntatore null per puntatori).
Per tipi non scalari (strutture, matrici, unioni), {0}
specifica che il primo primo elemento dell'oggetto è inizializzato su zero. Per strutture contenenti strutture, matrici di strutture e così via, questo viene applicato in modo ricorsivo, quindi il primo elemento scalare viene impostato sullo zero, come appropriato per il tipo. Come in qualsiasi inizializzatore, tutti gli elementi non specificati sono impostati su zero.
Parentesi graffe intermedie ({
, }
) può essere omesso; ad esempio entrambi sono validi ed equivalenti:
int arr[2][2] = { { 1, 2 }, {3, 4} };
int arr[2][2] = { 1, 2, 3, 4 };
motivo per cui non è necessario scrivere, ad esempio, { { 0 } }
per un tipo il cui primo elemento non è scalare.
Così questo:
some_type obj = { 0 };
è un modo abbreviato di inizializzare obj
su zero, il che significa che ciascun oggetto secondario scalare di obj
è impostato su 0
se è un numero intero, 0.0
se è in virgola mobile o un puntatore nullo se è un puntatore.
Le regole sono simili per C++.
Nel tuo caso particolare, poiché stai assegnando valori a sexi.cbSize
e così via, è chiaro che SHELLEXECUTEINFO
è un tipo di struttura o classe (o forse un sindacato, ma probabilmente no), quindi non tutto ciò si applica, ma come ho detto { 0 }
è un linguaggio comune che può essere utilizzato in situazioni più generali.
Questo è non (necessariamente) equivalente all'uso di memset
per impostare la rappresentazione dell'oggetto su all-bit-zero. Né in virgola mobile 0.0
né un puntatore nullo è necessariamente rappresentato come all-bit-zero e un { 0 }
l'inizializzatore non imposta necessariamente i byte di riempimento su un valore particolare. Sulla maggior parte dei sistemi, tuttavia, è probabile che abbia lo stesso effetto.
È passato un po 'di tempo da quando ho lavorato in c/c ++ ma IIRC, lo stesso collegamento può essere usato anche per le matrici.
Mi sono sempre chiesto, perché dovresti usare qualcosa del genere
struct foo bar = { 0 };
Ecco un caso di prova per spiegare:
check.c
struct f {
int x;
char a;
} my_zero_struct;
int main(void)
{
return my_zero_struct.x;
}
Compilo con gcc -O2 -o check check.c
e quindi emette la tabella dei simboli con readelf -s check | sort -k 2
(questo è con gcc 4.6.3 su Ubuntu 12.04.2 su un sistema x64). Estratto:
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
La parte importante qui è che my_zero_struct
è dopo __bss_start
. La sezione ".bss" in un programma C è la sezione di memoria che è impostata su zero prima che main
sia chiamato vedi - Wikipedia su .bss .
Se si modifica il codice sopra in:
} my_zero_struct = { 0 };
Quindi l'eseguibile "check" risultante appare esattamente lo stesso almeno con il compilatore gcc 4.6.3 su Ubuntu 12.04.2; il my_zero_struct
è ancora nel .bss
e quindi verrà inizializzato automaticamente a zero, prima che venga chiamato main
.
Suggerimenti nei commenti, secondo cui un memset
potrebbe inizializzare la struttura "completa" non costituisce un miglioramento, poiché .bss
la sezione viene cancellata completamente, il che significa anche che la struttura "full" è impostata su zero.
potrebbe essere che lo standard del linguaggio C non menziona nulla di tutto ciò, ma in un compilatore C del mondo reale non ho mai visto un comportamento diverso.