Struct and union initialization
Lors de l'initialisation d'un objet de type struct ou union , l'initialiseur doit être une liste non vide, (jusqu'à C23) entre accolades, séparée par des virgules, des initialiseurs pour les membres :
=
{
expression
,
...
}
|
(1) | (jusqu'en C99) | |||||||
=
{
designator
(optionnel)
expression
,
...
}
|
(2) | (depuis C99) | |||||||
=
{
}
|
(3) | (depuis C23) | |||||||
où le
désignateur
est une séquence (séparée par des espaces ou adjacente) de désignateurs individuels de membres de la forme
.
membre
et
désignateurs de tableau
de la forme
[
index
]
.
Tous les membres qui ne sont pas initialisés explicitement sont empty-initialized .
Table des matières |
Explication
Lors de l'initialisation d'une union , la liste d'initialisation ne doit comporter qu'un seul membre, qui initialise le premier membre de l'union sauf si un initialiseur désigné est utilisé (depuis C99) .
union { int x; char c[4]; } u = {1}, // rend u.x actif avec la valeur 1 u2 = { .c={'\1'} }; // rend u2.c actif avec la valeur {'\1','\0','\0','\0'}
Lors de l'initialisation d'une struct , le premier initialiseur dans la liste initialise le premier membre déclaré (sauf si un désignateur est spécifié) (depuis C99) , et tous les initialiseurs suivants sans désignateurs (depuis C99) initialisent les membres de la structure déclarés après celui initialisé par l'expression précédente.
struct point {double x,y,z;} p = {1.2, 1.3}; // p.x=1.2, p.y=1.3, p.z=0.0 div_t answer = {.quot = 2, .rem = -1 }; // l'ordre des éléments dans div_t peut varier
|
Un désignateur entraîne l'initialisation du membre de structure décrit par le désignateur par l'initialisateur suivant. L'initialisation se poursuit ensuite dans l'ordre de déclaration, en commençant par l'élément suivant déclaré après celui décrit par le désignateur. struct {int sec,min,hour,day,mon,year;} z = {.day=31,12,2014,.sec=30,15,17}; // initializes z to {30,15,17,31,12,2014} |
(depuis C99) |
C'est une erreur de fournir plus d'initialiseurs que de membres.
Initialisation imbriquée
Si les membres de la structure ou de l'union sont des tableaux, des structures ou des unions, les initialiseurs correspondants dans la liste d'initialiseurs entre accolades sont tous les initialiseurs valides pour ces membres, sauf que leurs accolades peuvent être omises comme suit :
Si l'initialiseur imbriqué commence par une accolade ouvrante, l'intégralité de l'initialiseur imbriqué jusqu'à son accolade fermante initialise l'objet membre correspondant. Chaque accolade ouvrante gauche établit un nouveau objet courant . Les membres de l'objet courant sont initialisés dans leur ordre naturel , sauf si des désignateurs sont utilisés (depuis C99) : les éléments de tableau dans l'ordre des indices, les membres de structure dans l'ordre de déclaration, uniquement le premier membre déclaré de toute union. Les sous-objets de l'objet courant qui ne sont pas explicitement initialisés par l'accolade fermante sont initialisés à vide .
struct example { struct addr_t { uint32_t port; } addr; union { uint8_t a8[4]; uint16_t a16[2]; } in_u; }; struct example ex = { // début de la liste d'initialisation pour struct example { // début de la liste d'initialisation pour ex.addr 80 // membre unique de la structure initialisé }, // fin de la liste d'initialisation pour ex.addr { // début de la liste d'initialisation pour ex.in_u {127,0,0,1} // initialise le premier élément de l'union } };
Si l'initialiseur imbriqué ne commence pas par une accolade ouvrante, seuls suffisamment d'initialiseurs de la liste sont pris en compte pour les éléments ou membres du tableau, de la structure ou de l'union membre ; tout initialiseur restant est laissé pour initialiser le membre de structure suivant :
struct example ex = {80, 127, 0, 0, 1}; // 80 initialise ex.addr.port // 127 initialise ex.in_u.a8[0] // 0 initialise ex.in_u.a8[1] // 0 initialise ex.in_u.a8[2] // 1 initialise ex.in_u.a8[3]
|
Lorsque les désignateurs sont imbriqués, les désignateurs des membres suivent les désignateurs des structs/unions/tableaux englobants. Dans toute liste d'initialisation entre crochets imbriquée, le désignateur le plus externe fait référence à l' objet courant et sélectionne le sous-objet à initialiser uniquement au sein de l' objet courant . struct example ex2 = { // current object is ex2, designators are for members of example .in_u.a8[0]=127, 0, 0, 1, .addr=80}; struct example ex3 = {80, .in_u={ // changes current object to the union ex.in_u 127, .a8[2]=1 // this designator refers to the member of in_u } }; Si un sous-objet est explicitement initialisé deux fois (ce qui peut se produire lorsque des désignateurs sont utilisés), l'initialiseur qui apparaît plus tard dans la liste est celui utilisé (l'initialiseur précédent peut ne pas être évalué) : Bien que tout sous-objet non initialisé soit initialisé implicitement, l'initialisation implicite d'un sous-objet ne remplace jamais l'initialisation explicite du même sous-objet si elle apparaît plus tôt dans la liste d'initialisation (choisissez clang pour observer la sortie correcte) :
Exécuter ce code
#include <stdio.h> typedef struct { int k; int l; int a[2]; } T; typedef struct { int i; T t; } S; T x = {.l = 43, .k = 42, .a[1] = 19, .a[0] = 18 }; // x initialized to {42, 43, {18, 19} } int main(void) { S l = { 1, // initializes l.i to 1 .t = x, // initializes l.t to {42, 43, {18, 19} } .t.l = 41, // changes l.t to {42, 41, {18, 19} } .t.a[1] = 17 // changes l.t to {42, 41, {18, 17} } }; printf("l.t.k is %d\n", l.t.k); // .t = x sets l.t.k to 42 explicitly // .t.l = 41 would zero out l.t.k implicitly } Sortie : l.t.k is 42 Cependant, lorsqu'un initialiseur commence par une accolade ouvrante, son objet courant est entièrement réinitialisé et tout initialiseur explicite antérieur pour l'un de ses sous-objets est ignoré : struct fred { char s[4]; int n; }; struct fred x[ ] = { { { "abc" }, 1 }, // inits x[0] to { {'a','b','c','\0'}, 1 } [0].s[0] = 'q' // changes x[0] to { {'q','b','c','\0'}, 1 } }; struct fred y[ ] = { { { "abc" }, 1 }, // inits y[0] to { {'a','b','c','\0'}, 1 } [0] = { // current object is now the entire y[0] object .s[0] = 'q' } // replaces y[0] with { {'q','\0','\0','\0'}, 0 } }; |
(depuis C99) |
Notes
La liste d'initialisation peut avoir une virgule finale, qui est ignorée.
struct {double x,y;} p = {1.0, 2.0, // virgule finale autorisée };
|
En C, la liste entre accolades des initialiseurs ne peut pas être vide (notez que le C++ autorise les listes vides, et notez également qu'une struct en C ne peut pas être vide) : |
(until C23) |
|
La liste d'initialisation peut être vide en C comme en C++ : |
(since C23) |
struct {int n;} s = {0}; // OK struct {int n;} s = {}; // Erreur jusqu'à C23 : la liste d'initialisation ne peut pas être vide // OK depuis C23 : s.n est initialisé à 0 struct {} s = {}; // Erreur : la structure ne peut pas être vide
|
Chaque expression dans la liste d'initialisation doit être une expression constante lors de l'initialisation d'agrégats de toute durée de stockage. |
(jusqu'en C99) |
|
Comme pour toute autre initialisation , chaque expression dans la liste d'initialisation doit être une expression constante lors de l'initialisation d'agrégats de durée de stockage statique ou thread-local (depuis C11) durée de stockage : static struct {char* p} s = {malloc(1)}; // error L' ordre d'évaluation des sous-expressions dans tout initialiseur est séquencé de manière indéterminée (mais pas en C++ depuis C++11) : int n = 1; struct {int x,y;} p = {n++, n++}; // non spécifié, mais comportement bien défini : // n est incrémenté deux fois dans un ordre arbitraire // p égal {1,2} et {2,1} sont tous deux valides |
(depuis C99) |
Exemple
|
Cette section est incomplète
Raison : plus d'exemples pratiques, peut-être initialiser certaines structures de socket |
#include <stdio.h> #include <time.h> int main(void) { char buff[70]; // les initialiseurs désignés simplifient l'utilisation des structs dont // l'ordre des membres n'est pas spécifié struct tm my_time = { .tm_year=2012-1900, .tm_mon=9, .tm_mday=9, .tm_hour=8, .tm_min=10, .tm_sec=20 }; strftime(buff, sizeof buff, "%A %c", &my_time); puts(buff); }
Sortie possible :
Sunday Sun Oct 9 08:10:20 2012
Références
- Norme C17 (ISO/CEI 9899:2018) :
-
- 6.7.9/12-39 Initialisation (p: 101-105)
- Norme C11 (ISO/IEC 9899:2011) :
-
- 6.7.9/12-38 Initialisation (p: 140-144)
- Norme C99 (ISO/CEI 9899:1999) :
-
- 6.7.8/12-38 Initialisation (p. 126-130)
- Norme C89/C90 (ISO/IEC 9899:1990) :
-
- 6.5.7 Initialisation
Voir aussi
|
Documentation C++
pour
Aggregate initialization
|