it-swarm.it

Cosa significa {0} quando si inizializza un oggetto?

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.

249
Mahmoud Al-Qudsi

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 con 1, ss.b con "asdf" e ss.c con il valore di un'espressione del modulo int(), ovvero 0.

Puoi trovare le specifiche complete su questo argomento qui

298
Don Neufeld

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.

89
Harold Ekstrom

Si noti che funziona anche un inizializzatore di aggregazione vuoto:

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

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.

11
snowcrash09

Lo uso anche per inizializzare stringhe ad es.

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

{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.

7
Keith Thompson

È passato un po 'di tempo da quando ho lavorato in c/c ++ ma IIRC, lo stesso collegamento può essere usato anche per le matrici.

3
µBio

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.

2
Ingo Blackman