Comment initialiser une structure conformément aux normes du langage de programmation C

466

Je veux initialiser un élément struct, divisé en déclaration et initialisation. Voici ce que j'ai:

typedef struct MY_TYPE {
  bool flag;
  short int value;
  double stuff;
} MY_TYPE;

void function(void) {
  MY_TYPE a;
  ...
  a = { true, 15, 0.123 }
}

Est-ce la façon de déclarer et d'initialiser une variable locale MY_TYPEconformément aux normes du langage de programmation C (C89, C90, C99, C11, etc.)? Ou y a-t-il quelque chose de mieux ou au moins de travailler?

Mise à jour J'ai fini par avoir un élément d'initialisation statique où je définissais chaque sous-élément en fonction de mes besoins.

reculer
la source
3
vous devriez vraiment accepter une meilleure réponse, je vois que vous avez dû utiliser un mauvais guide de codage, mais vous ne devriez toujours pas suggérer à d'autres personnes que c'est la bonne façon de le faire.
Karoly Horvath
@KarolyHorvath bien, la plupart des bonnes réponses sont spécifiques à C99. Peut-être que ma question est un doublon de stackoverflow.com/questions/6624975/… ?
grincer des dents le
si c'était votre intention initiale, alors probablement oui, mais alors 1) les votes seraient très trompeurs. 2) à partir des premiers résultats de recherche, c'est le seul qui montre la manière C99 ..... il serait préférable de réutiliser cette page pour la démonstration C99 ... (apparemment, les gens ont commencé à lier cette page pour montrer comment le faire)
Karoly Horvath
10
Il est intéressant de noter que la réponse acceptée (et fortement votée) ne répond pas réellement à la question, même telle qu'elle a été publiée à l'origine. Les initialiseurs désignés ne résolvent pas le problème de l'OP, qui consiste à séparer la déclaration de l'initialisation. Pour le C d'avant 1999, la seule vraie solution est d'assigner à chaque membre; pour C99 et plus tard, un littéral composé, comme dans la réponse de CesarB, est la solution. (Bien sûr, un véritable initialiseur, avec ou sans désignateurs, serait encore mieux, mais apparemment l'OP était équipé d'un très mauvais standard de codage.)
Keith Thompson
32
À strictement parler, le terme «ANSI C» fait désormais référence à la norme ISO 2011, que l'ANSI a adoptée. Mais dans la pratique, le terme "ANSI C" se réfère couramment à la norme (officiellement obsolète) de 1989. Par exemple, "gcc -ansi" applique toujours la norme de 1989. Étant donné que c'est l'ISO qui a publié les normes 1990, 1999 et 2011, il est préférable d'éviter le terme "ANSI C" et de se référer à la date de la norme s'il y a une possibilité de confusion.
Keith Thompson

Réponses:

728

Dans (ANSI) C99, vous pouvez utiliser un initialiseur désigné pour initialiser une structure:

MY_TYPE a = { .flag = true, .value = 123, .stuff = 0.456 };

Modifier: les autres membres sont initialisés à zéro: "Les membres de champ omis sont implicitement initialisés de la même manière que les objets qui ont une durée de stockage statique." ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )

philant
la source
87
super merci. vous pouvez même les imbriquer: static oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
orion elenzil
4
Cela ne répond pas du tout à la question. OP souhaite séparer l'initialisation de la déclaration.
osvein
3
C99! = ANSI C - Cela ne fonctionne donc pas en C89, ni en C90.
Tim Wißmann
3
C99 est ANSI C, voyez ceci
Cutberto Ocampo
3
@CutbertoOcampo Je comprends ce qui s'est passé maintenant. La question initiale demandait des solutions en C ANSI, il voulait évidemment des réponses pour C89 uniquement. En 2016, la question a été modifiée et "ANSI C" a été supprimé, ce qui rend difficile de comprendre pourquoi cette réponse et ces commentaires mentionnent "(ANSI) C99".
Étienne
198

Vous pouvez le faire avec un littéral composé . Selon cette page, cela fonctionne en C99 (qui compte également comme ANSI C ).

MY_TYPE a;

a = (MY_TYPE) { .flag = true, .value = 123, .stuff = 0.456 };
...
a = (MY_TYPE) { .value = 234, .stuff = 1.234, .flag = false };

Les désignations dans les initialiseurs sont facultatives; vous pouvez également écrire:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };
CesarB
la source
Ainsi, les initialiseurs désignés sont pour quand vous déclarez et définissez en même temps, mais les littéraux composés sont pour quand vous définissez une variable déjà déclarée?
Geremia
@Geremia vous devez d'abord définir la structure dans les deux cas, mais avec des initialiseurs désignés, vous rendez le code plus lisible et plus résistant aux erreurs, en cas de changements dans la structure.
hoijui
2
C'est un peu délicat. Plusieurs fois (certaines réussies), j'ai essayé d'utiliser des initialiseurs désignés. Cependant, il est maintenant devenu clair (grâce à votre exemple et à @ philant) qu'il doit exister un transtypage si l'initialiseur n'est pas utilisé au moment de la création de l'objet. Cependant, j'ai maintenant également appris que si les initialiseurs sont utilisés à un moment autre que la création d'objet (comme dans votre exemple), il est alors appelé un littéral composé , pas strictement un initialiseur désigné . ideone.com/Ze3rnZ
sherrellbc
93

Je vois que vous avez déjà reçu une réponse sur ANSI C 99, donc je vais jeter un os sur ANSI C 89. ANSI C 89 vous permet d'initialiser une structure de cette façon:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = { 5, 2.2, "George" };
    return 0;
}

Une chose importante à retenir, au moment où vous initialisez même un objet / variable dans la structure, toutes ses autres variables seront initialisées à la valeur par défaut.

Si vous n'initialisez pas les valeurs dans votre structure, toutes les variables contiendront des "valeurs inutiles".

Bonne chance!

Ron Nuni
la source
3
Est-il possible d'utiliser des variables dans l'initialisation?
Cyan
2
@Cyan C'est pour les objets avec une durée de stockage automatique. Voir 6.7.9 13) dans open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Pour les objets globaux est à peu près limité aux littéraux. Vous ne pouvez même pas utiliser d'autres objets globaux, même s'ils sont const.
PSkocik
Donc, la seule différence avec C 99 est que vous ne pouvez pas spécifier les désignations?
Akaisteph7
42

a = (MYTYPE){ true, 15, 0.123 };

ferait bien en C99

qrdl
la source
1
Je pense honnêtement que la réponse est la plus utile, mais peu importe.
user12211554
22

Vous l'avez presque compris ...

MY_TYPE a = { true,15,0.123 };

Une recherche rapide sur 'struct initialize c' me le montre

Kieveli
la source
1
Je pense que cela est vrai pour le C standard, mais pas pour l'ANSI C (99?). Je suis également lié à des règles de codage qui ne me permettront pas de faire la déclaration et l'initialisation à la fois, donc je dois le diviser et initialiser chaque sous-élément.
grincer des dents
8
L'initialisation ne peut avoir lieu qu'au point de déclaration. Voilà ce que signifie «initialiser». Sinon, vous autorisez une valeur indéfinie à être la valeur initiale de votre variable / structure, puis attribuez une valeur plus tard.
Kieveli
8
euh ... Vous avez des règles de codage qui sont délibérément mauvaises? Vous devriez peut-être présenter le gars qui les a écrits à "L'acquisition de ressources est l'initialisation" ( en.wikipedia.org/wiki/RAII )
James Curran
Croyez-moi, je ne peux vraiment, vraiment, pas changer un peu de ces règles à ce stade. C'est horrible de le coder. Et il y a beaucoup d'autres règles dès les années 70, comme les en-têtes de code statiques avec des informations de gestion comme le numéro de révision. Changements pour chaque version, même si la source n'a pas changé ...
grincer des dents
19

La norme de langage de programmation C ISO / IEC 9899: 1999 (communément appelée C99) permet d'utiliser un initialiseur désigné pour initialiser les membres d'une structure ou d'une union comme suit:

MY_TYPE a = { .stuff = 0.456, .flag = true, .value = 123 };

Il est défini dans la paragraph 7section 6.7.8 Initializationde la norme ISO / IEC 9899: 1999 comme:

Si un désignateur a le formulaire
. identifiant,
alors l'objet courant (défini ci-dessous) doit avoir une structure ou un type d'union et l'identifiant doit être le nom d'un membre de ce type.

Notez que paragraph 9de la même section déclare que:

Sauf indication contraire explicite, aux fins du présent paragraphe, les membres non nommés d'objets de structure et de type d'union ne participent pas à l'initialisation. Les membres sans nom des objets de structure ont une valeur indéterminée même après l'initialisation.

Dans la mise en œuvre de GNU GCC, cependant, les membres omis sont initialisés en tant que valeur appropriée de type zéro ou de type zéro. Comme indiqué dans la section 6.27 Initialiseurs désignés de la documentation GNU GCC:

Les membres de champ omis sont implicitement initialisés de la même manière que les objets qui ont une durée de stockage statique.

Le compilateur Microsoft Visual C ++ devrait prendre en charge les initialiseurs désignés depuis la version 2013 selon le blog officiel C ++ Conformance Roadmap . Paragraphe Initializing unions and structsdes initialiseurs article dans la documentation MSDN Visual Studio suggère que les membres non nommés sont initialisés à des valeurs appropriées de type zéro similaires à GNU GCC.

La norme ISO / IEC 9899: 2011 (communément appelée C11) qui avait remplacé ISO / IEC 9899: 1999 conserve les initialiseurs désignés dans la section 6.7.9 Initialization. Il conserve égalementparagraph 9 inchangé.

La nouvelle norme ISO / IEC 9899: 2018 (communément appelée C18) qui avait remplacé ISO / IEC 9899: 2011 conserve les initialiseurs désignés dans la section 6.7.9 Initialization. Il reste également paragraph 9inchangé.

PF4Public
la source
3
Au départ, j'ai suggéré cela comme modification de la réponse acceptée, mais elle a été rejetée. Ainsi je le poste comme réponse.
PF4Public
Je crois que «membre sans nom» ne signifie pas «champs omis», mais plutôt «membres anonymes» comme le syndicat en struct thing { union { char ch; int i; }; };. La norme dit plus tard: "S'il y a moins d'initialiseurs dans une liste entre accolades qu'il n'y a d'éléments ou de membres d'un agrégat, ou moins de caractères dans un littéral de chaîne utilisé pour initialiser un tableau de taille connue qu'il n'y a d'éléments dans le tableau, le reste de l'agrégat doit être initialisé implicitement comme les objets qui ont une durée de stockage statique. "
emersion
14

comme l'a dit Ron Nuni:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = {5, 2.2, "George"};
    return 0;
}

Une chose importante à retenir: au moment où vous initialisez même un objet / variable dans la structure, toutes ses autres variables seront initialisées à la valeur par défaut.

Si vous n'initialisez pas les valeurs dans votre structure (c'est-à-dire si vous déclarez simplement cette variable), toutes variable.memberscontiendront des "valeurs de poubelle", seulement si la déclaration est locale!

Si la déclaration est globale ou statique (comme dans ce cas), tous les non initialisés variable.membersseront initialisés automatiquement à:

  • 0 pour les nombres entiers et virgule flottante
  • '\0'pour char(bien sûr, c'est la même chose que 0, etchar c'est un type entier)
  • NULL pour les pointeurs.
r_goyal
la source
Intéressant sur le local / global, cela s'applique également aux valeurs de temps struct tm. J'en avais un local et dans un cas, l'heure d'été était activée, dans un autre, ce n'était pas le cas, pas à cause de tout ce que j'ai fait. Je n'utilisais pas DST (le champ tm_isdst), c'était à partir de strptime, mais quand j'ai converti en time_t certains étaient à une heure de congé.
Alan Corey
3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}
robert
la source
25
une fois que j'ai entendu «l'initialisation est différente de l'affectation». n'est-ce pas une affectation plutôt qu'une initialisation?
Hayri Uğur Koltuk
2

Si MS n'a pas mis à jour vers C99, MY_TYPE a = {true, 15,0.123};

eddyq
la source
2

J'ai trouvé une autre façon d'initialiser les structures.

La structure:

typedef struct test {
    int num;
    char* str;
} test;

Initialisation:

test tt = {
    num: 42,
    str: "nice"
};

Selon la documentation de GCC, cette syntaxe est obsolète depuis GCC 2.5 .

MichaelWang
la source
2

Je n'ai aimé aucune de ces réponses, alors j'ai fait la mienne. Je ne sais pas si c'est ANSI C ou pas, c'est juste GCC 4.2.1 dans son mode par défaut. Je ne me souviens jamais du bracketing, donc je commence avec un sous-ensemble de mes données et je me bats avec les messages d'erreur du compilateur jusqu'à ce qu'il se ferme. La lisibilité est ma première priorité.

    // in a header:
    typedef unsigned char uchar;

    struct fields {
      uchar num;
      uchar lbl[35];
    };

    // in an actual c file (I have 2 in this case)
    struct fields labels[] = {
      {0,"Package"},
      {1,"Version"},
      {2,"Apport"},
      {3,"Architecture"},
      {4,"Bugs"},
      {5,"Description-md5"},
      {6,"Essential"},
      {7,"Filename"},
      {8,"Ghc-Package"},
      {9,"Gstreamer-Version"},
      {10,"Homepage"},
      {11,"Installed-Size"},
      {12,"MD5sum"},
      {13,"Maintainer"},
      {14,"Modaliases"},
      {15,"Multi-Arch"},
      {16,"Npp-Description"},
      {17,"Npp-File"},
      {18,"Npp-Name"},
      {19,"Origin"}
    };

Les données peuvent commencer la vie comme un fichier délimité par des tabulations que vous recherchez-remplacez pour masser dans autre chose. Oui, c'est des trucs Debian. Donc une paire extérieure de {} (indiquant le tableau), puis une autre paire pour chaque structure à l'intérieur. Avec des virgules entre. Mettre des choses dans un en-tête n'est pas strictement nécessaire, mais j'ai environ 50 éléments dans ma structure, donc je les veux dans un fichier séparé, à la fois pour garder le désordre de mon code et donc c'est plus facile à remplacer.

Alan Corey
la source
3
Il n'y avait aucun doute sur l'initialisation du tableau de structures! Je veux dire, votre réponse contient des frais généraux.
Victor Signaevskyi
Je suppose que mon point était de déclarer la structure avec des valeurs déjà dedans vs d'utiliser = pour définir chaque valeur plus tard.
Alan Corey
Ce principe s'applique que ce soit une structure ou un tableau d'entre eux. Utiliser = n'est pas vraiment l'initialisation, je ne pense pas.
Alan Corey
0

La structure en C peut être déclarée et initialisée comme ceci:

typedef struct book
{
    char title[10];
    char author[10];
    float price;
} book;

int main() {
    book b1={"DS", "Ajay", 250.0};

    printf("%s \t %s \t %f", b1.title, b1.author, b1.price);

    return 0;
}
Ajay Parashar
la source
0

J'ai déjà lu la documentation de Microsoft Visual Studio 2015 pour l'initialisation des types d'agrégats , toutes les formes d'initialisation avec{...} sont expliquées, mais l'initialisation avec point, nommée `` désignateur '' n'y est pas mentionnée. Cela ne fonctionne pas non plus.

Le standard C99 chapitre 6.7.8 Initialisation explique la possibilité des désignateurs, mais dans mon esprit ce n'est pas vraiment clair pour les structures complexes. La norme C99 en pdf .

Dans mon esprit, il vaut peut-être mieux

  1. Utilisez le = {0}; initialisation pour toutes les données statiques. C'est moins d'effort pour le code machine.
  2. Utilisez des macros pour l'initialisation, par exemple

    typedef MyStruct_t{ int x, int a, int b; } MyStruct; define INIT_MyStruct(A,B) { 0, A, B}

La macro peut être adaptée, sa liste d'arguments peut être indépendante du contenu struct modifié. Il convient que moins d'éléments soient initialisés. Il convient également pour les structures imbriquées. 3. Un formulaire simple est: Initialiser dans un sous-programme:

void init_MyStruct(MyStruct* thiz, int a, int b) {
  thiz->a = a; thiz->b = b; }

Cette routine ressemble à ObjectOriented en C. Utilisez thiz, pas thispour la compiler avec C ++ aussi!

MyStruct data = {0}; //all is zero!
init_MyStruct(&data, 3, 456);
Hartmut Schorrig
la source
0

Je cherchais un bon moyen d'initialiser ma structure, et je dois utiliser le ci-dessous (C99). Cela me permet d'initialiser une seule structure ou un tableau de structures de la même manière que les types simples.

typedef struct {
    char *str;
    size_t len;
    jsmntok_t *tok;
    int tsz;
} jsmn_ts;

#define jsmn_ts_default (jsmn_ts){NULL, 0, NULL, 0}

Cela peut être utilisé dans le code comme:

jsmn_ts mydata = jsmn_ts_default; /* initialization of a single struct */

jsmn_ts myarray[10] = {jsmn_ts_default, jsmn_ts_default}; /* initialization of
                                                    first 2 structs in the array */
div0man
la source
Mais cela n'initialise pas un tableau de structures aux valeurs par défaut. Il initialise la première structure du tableau aux valeurs indiquées dans jsmn_ts_default, mais les autres structures sont initialisées comme si le tableau avait une durée de stockage statique, ce qui signifie que les membres sont initialisés à NULLet 0. Mais si vous passez à, #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}vous verrez que seul le premier élément du tableau est ainsi initialisé.
ex nihilo
Oui, je reviens tout juste de plus de tests, je voulais modifier le post :) btw, le même comportement se produit avec int a[10] = {1};Seul le 1er élément sera==1
div0man