web-dev-qa-db-fra.com

Que signifie {0} lors de l'initialisation d'un objet?

Quand {0} est utilisé pour initialiser un objet, que signifie-t-il? Je ne trouve aucune référence à {0} n'importe où, et à cause des accolades, les recherches Google ne sont pas utiles.

Exemple de code:

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

Sans cela, le code ci-dessus plantera à l'exécution.

249
Mahmoud Al-Qudsi

Ce qui se passe ici s'appelle initialisation globale . Voici la définition (abrégée) d'un agrégat de la section 8.5.1 de la spécification ISO:

Un agrégat est un tableau ou une classe sans constructeurs déclarés par l'utilisateur, sans membres de données privés ou protégés non statiques, sans classes de base et sans fonctions virtuelles.

Maintenant, utiliser {0} Pour initialiser un agrégat comme ceci est fondamentalement une astuce pour 0 La chose entière. En effet, lorsque vous utilisez l'initialisation d'agrégat , vous n'avez pas à spécifier tous les membres et la spécification requiert que tous les membres non spécifiés soient initialisés par défaut, ce qui signifie que à 0 pour les types simples.

Voici la citation pertinente de la spécification:

S'il y a moins d'initialisateurs dans la liste que de membres dans l'agrégat, chaque membre non explicitement initialisé doit être initialisé par défaut. Exemple:

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

initialise ss.a avec 1, ss.b avec "asdf" et ss.c avec la valeur d'une expression de la forme int(), c'est-à-dire 0.

Vous pouvez trouver la spécification complète sur ce sujet ici

298
Don Neufeld

Une chose à prendre en compte est que cette technique ne mettra pas les octets de remplissage à zéro. Par exemple:

struct foo
{
    char c;
    int  i;
};

foo a = {0};

N'est-ce pas la même chose que:

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

Dans le premier cas, les octets de pad entre c et i ne sont pas initialisés. Pourquoi voudriez-vous vous en soucier? Eh bien, si vous sauvegardez ces données sur un disque ou si vous les envoyez sur un réseau ou autre, vous pourriez avoir un problème de sécurité.

89
Harold Ekstrom

Notez qu'un initialiseur d'agrégat vide fonctionne également:

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

En réponse à la raison pour laquelle ShellExecuteEx() se bloque: votre structure SHELLEXECUTEINFO "sexi" a de nombreux membres et vous n'en initialisez que quelques-uns.

Par exemple, le membre sexi.lpDirectory Peut pointer n'importe où, mais ShellExecuteEx() essayera toujours de l'utiliser, ce qui entraînera une violation d'accès à la mémoire.

Lorsque vous incluez la ligne:

SHELLEXECUTEINFO sexi = {0};

avant le reste de votre structure, vous dites au compilateur de mettre à zéro tous les membres de la structure avant d'initialiser ceux qui vous intéressent. ShellExecuteEx() sait que si sexi.lpDirectory Vaut zéro, il devrait l'ignorer.

11
snowcrash09

Je l'utilise aussi pour initialiser des chaînes, par exemple.

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

{0} est un initialiseur valide pour tout type (objet complet), à la fois en C et en C++. C'est un langage courant utilisé pour initialiser un objet à zéro (lisez la suite pour voir ce que cela signifie).

Pour les types scalaires (types arithmétique et pointeur), les accolades sont inutiles, mais elles sont explicitement autorisées. Citant le N1570 draft de la norme ISO C, section 6.7.9:

L'initialiseur d'un scalaire doit être une expression unique, éventuellement entre accolades.

Il initialise l'objet à zéro (0 pour les entiers, 0.0 pour virgule flottante, un pointeur nul pour les pointeurs).

Pour les types non scalaires (structures, tableaux, unions), {0} spécifie que le premier élément de l'objet est initialisé à zéro. Pour les structures contenant des structures, des tableaux de structures, etc., ceci est appliqué de manière récursive. Le premier élément scalaire est donc mis à zéro, comme il convient pour le type. Comme dans tout initialiseur, tous les éléments non spécifiés sont mis à zéro.

Accolades intermédiaires ({, }) peut être omis; Par exemple, les deux sont valides et équivalents:

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

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

c'est pourquoi vous n'avez pas à écrire, par exemple, { { 0 } } pour un type dont le premier élément est non scalaire.

Donc ça:

some_type obj = { 0 };

est un moyen abrégé d'initialiser obj à zéro, ce qui signifie que chaque sous-objet scalaire de obj est défini sur 0 si c'est un entier, 0.0 s'il s'agit d'une virgule flottante ou d'un pointeur nul s'il s'agit d'un pointeur.

Les règles sont similaires pour C++.

Dans votre cas particulier, puisque vous attribuez des valeurs à sexi.cbSize et ainsi de suite, il est clair que SHELLEXECUTEINFO est un type de structure ou de classe (ou éventuellement une union, mais probablement pas), donc tout cela ne s'applique pas, mais comme je l'ai dit { 0 } est un langage courant qui peut être utilisé dans des situations plus générales.

Ceci ( n'est pas (nécessairement) équivalent à utiliser memset pour définir la représentation de l'objet sur all-bits-zero. Ni l'une ni l'autre virgule flottante 0.0 ni un pointeur nul n'est nécessairement représenté par zéro entier, et un { 0 } initializer ne définit pas nécessairement les octets de remplissage sur une valeur particulière. Sur la plupart des systèmes, cependant, cela aura probablement le même effet.

7
Keith Thompson

Cela fait longtemps que je n'ai pas travaillé dans c/c ++ mais IIRC, le même raccourci peut également être utilisé pour les tableaux.

3
µBio

Je me suis toujours demandé pourquoi vous devriez utiliser quelque chose comme

struct foo bar = { 0 };

Voici un cas de test à expliquer:

check.c

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

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

Je compile avec gcc -O2 -o check check.c Puis édite la table des symboles avec readelf -s check | sort -k 2 (Il s’agit de gcc 4.6.3 sur Ubuntu 12.04.2 sur un système x64). Extrait:

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 partie importante ici est que my_zero_struct Est après __bss_start. La section ".bss" dans un programme C est la section de mémoire qui est mise à zéro avant que main soit appelé see - wikipedia sur .bss .

Si vous modifiez le code ci-dessus en:

} my_zero_struct = { 0 };

Ensuite, l'exécutable "chèque" résultant ressemble exactement au moins avec le compilateur gcc 4.6.3 sur ubuntu 12.04.2; le my_zero_struct est toujours dans la section .bss et sera donc automatiquement initialisé à zéro avant que main ne soit appelé.

Les indices dans les commentaires, qu'un memset pourrait initialiser la structure "complète" n'est également pas une amélioration, car la section .bss Est entièrement effacée, ce qui signifie également que la structure "complète" est définie sur zéro .

Il se peut que le standard de langage C ne mentionne rien de tout cela, mais dans un monde réel, le compilateur C n'a jamais vu un comportement différent.

2
Ingo Blackman