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

252

Quand {0}est utilisé pour initialiser un objet, qu'est-ce que cela signifie? Je ne trouve aucune référence à {0}quelque part, et à cause des accolades bouclées, 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 lors de l'exécution.

Mahmoud Al-Qudsi
la source

Réponses:

302

Ce qui se passe ici s'appelle l' initialisation agrégée . 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 non statiques privés ou protégés, sans classes de base et sans fonctions virtuelles.

Maintenant, utiliser {0}pour initialiser un agrégat comme celui-ci est fondamentalement une astuce pour 0le tout. En effet, lorsque vous utilisez l'initialisation agrégée, vous n'avez pas besoin de 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 défini sur 0pour les types simples.

Voici la citation pertinente de la spécification:

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

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

initialise ss.aavec 1, ss.bavec "asdf"et ss.cavec 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

Don Neufeld
la source
15
Excellente réponse. Je voulais juste ajouter que l'initialisation d'un agrégat avec {0} est identique à l'initialisation avec simplement {}. Peut-être que le premier rend plus évident que les types intégrés sont mis à zéro.
James Hopkin
7
Certains compilateurs s'étouffent avec {}, c'est pourquoi {0} est utilisé
Branan
10
Pas tout à fait .. En C ++, si le premier membre ne peut pas être construit à zéro, {0} ne fonctionnera pas. Ex: struct A {B b; int i; char c; }; struct B {B (); B (chaîne); }; A a = {}; // cette instruction ne peut pas être réécrite en 'A a = {0}'.
Aaron
25
@Branan, c'est parce qu'en C "{}" n'est pas valide. En C ++ c'est le cas. @ don.neufeld, cela a changé avec C ++ 03 (initialisé par défaut -> initialisé par valeur). La citation cite la norme C ++ 98, gardez à l'esprit.
Johannes Schaub - litb
3
Donc, cela remplace-t-il la nécessité d'utiliser ZeroMemory () (dans VC ++)?
Ray
89

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

struct foo
{
    char c;
    int  i;
};

foo a = {0};

N'est pas la même chose que:

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

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

Harold Ekstrom
la source
13
Bien sûr, ce n'est un problème de sécurité que si vous «écrivez (f, & a, sizeof (a))», ce qui peut produire un codage de fichier différent sur différents processeurs / compilateurs. Une sortie bien formatée serait sûre sans le memset.
Aaron
3
De plus, si vous envoyez des informations sur le réseau, vous définissez toujours l'alignement à emballer. De cette façon, vous obtenez le moins d'octets de remplissage supplémentaires possible.
Mark Kegel
18
Il convient de noter que bien que la spécification ne nécessite pas l'initialisation du rembourrage, tout compilateur sensé le fera, car il ne coûte que du temps pour l'initialiser "autour".
Tomas
4
J'aimerais contester l'utilisation des mots «n'est pas». Les octets de remplissage sont définis par l'implémentation. Le compilateur est libre de transformer foo a = {0} en memset (& a, 0, sizeof (a)) à sa guise. Il n'est pas nécessaire de "sauter" les octets de remplissage et de définir uniquement foo.c et foo.i. +1 pour le bogue de sécurité (potentiel)
SecurityMatt
3
@Leushenko point 19 dit "tous les sous-objets qui ne sont pas initialisés explicitement", donc je pencherais pour que le point 21 (que vous avez cité) soit bâclé. Ce serait vraiment bizarre si la spécification permettait de ne pas initialiser le remplissage jusqu'au moment du dernier initialiseur, après quoi le remplissage devait être mis à zéro, d'autant plus que les initialiseurs peuvent apparaître dans le désordre lorsque des initialiseurs désignés sont utilisés.
MM
20

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

SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};
dalle
la source
11

En réponse à pourquoi ShellExecuteEx()se bloque: votreSHELLEXECUTEINFO structure "sexi" a de nombreux membres et vous n'en initialisez que certains.

Par exemple, le membre sexi.lpDirectorypeut pointer n'importe où, mais ShellExecuteEx()essaiera toujours de l'utiliser, vous obtiendrez donc une violation d'accès à la mémoire.

Lorsque vous incluez la ligne:

SHELLEXECUTEINFO sexi = {0};

avant le reste de la configuration 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.lpDirectoryzéro, il doit l'ignorer.

snowcrash09
la source
7

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

char mytext[100] = {0};
Adam Pierce
la source
5
Bien que, bien sûr, si mytext est utilisé comme une chaîne, char mytext [100]; mytext [0] = '\ 0'; aurait le même effet de donner une chaîne vide, mais ne ferait que mettre à zéro le premier octet de l'implémentation.
Chris Young
@Chris: J'ai souvent souhaité qu'il y ait une syntaxe pour l'initialisation partielle des objets. Pouvoir "déclarer et initialiser" x, puis faire de même avec y, puis z, est beaucoup plus agréable que de devoir déclarer x, y et z, puis initialiser x, y et z, mais initialiser 100 octets alors que seulement il faut en fait une initialisation semble plutôt inutile.
supercat
7

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

Pour les types scalaires (types arithmétiques et pointeurs), les accolades ne sont pas nécessaires, mais elles sont explicitement autorisées. Citant le projet N1570 de la norme ISO C, section 6.7.9:

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

Il initialise l'objet à zéro ( 0pour les entiers, 0.0pour les virgules flottantes, 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., cela est appliqué de manière récursive, de sorte que le premier élément scalaire est défini sur zéro, en fonction du type. Comme dans tout initialiseur, tous les éléments non spécifiés sont mis à zéro.

Les accolades intermédiaires ( {, }) peuvent être omises; 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 une manière abrégée d'initialiser objà zéro, ce qui signifie que chaque sous-objet scalaire de objest défini 0si c'est un entier, 0.0s'il est à virgule flottante, ou un pointeur nul si c'est un pointeur.

Les règles sont similaires pour C ++.

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

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

Keith Thompson
la source
1
En C ++, {0}n'est pas un initialiseur valide pour un objet sans constructeur acceptant 0; ni pour un agrégat dont le premier élément est en tant que tel (ou un agrégat sans élément)
MM
3

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

µBio
la source
2

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

struct foo bar = { 0 };

Voici un cas de test pour 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.cpuis affiche la table des symboles avec readelf -s check | sort -k 2(c'est avec 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, c'est my_zero_structaprès __bss_start. La section ".bss" dans un programme C est la section de mémoire qui est mise à zéro avant d' main être appelée voir wikipedia sur .bss .

Si vous modifiez le code ci-dessus pour:

} my_zero_struct = { 0 };

Ensuite, l'exécutable "check" résultant a exactement la même apparence au moins avec le compilateur gcc 4.6.3 sur ubuntu 12.04.2; le my_zero_structest toujours dans la .bsssection et il sera donc automatiquement initialisé à zéro, avant d' mainêtre appelé.

Des indications dans les commentaires, selon lesquelles une memsetinitialisation de la structure "complète" n'est pas non plus une amélioration, car la .bsssection 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 du langage C ne mentionne rien de tout cela, mais dans un compilateur C réel, je n'ai jamais vu un comportement différent.

Ingo Blackman
la source
Les variables globales et statiques sont toujours initialisées par défaut à 0 ou ctor par défaut. Mais si vous déclarez l'instance de f localement, vous pouvez obtenir des résultats différents.
Logman
0

C'est du sucre syntaxique pour initialiser votre structure entière à des valeurs vides / nulles / nulles.

Version longue

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;

Version courte

SHELLEXECUTEINFO sexi = {0};

N'était-ce pas beaucoup plus facile?

C'est aussi bien parce que:

  • vous n'avez pas à traquer tous les membres et à l'initialiser
  • vous n'avez pas à vous soucier de ne pas initialiser de nouveaux membres lorsqu'ils seront ajoutés ultérieurement
  • vous n'avez pas à appelerZeroMemory
Ian Boyd
la source
-5

{0} est un tableau anonyme contenant son élément comme 0.

Ceci est utilisé pour initialiser un ou tous les éléments du tableau avec 0.

par exemple int arr [8] = {0};

Dans ce cas, tous les éléments de arr seront initialisés à 0.

nitin prajapati
la source
4
{0}n'est pas un tableau anonyme. Ce n'est même pas une expression. C'est un initialiseur.
Keith Thompson