Namespaces
Variants

Struct declaration

From cppreference.net

Une structure est un type constitué d'une séquence de membres dont le stockage est alloué dans une séquence ordonnée (par opposition à une union, qui est un type constitué d'une séquence de membres dont le stockage se chevauche).

Le spécificateur de type pour une structure est identique au union spécificateur de type, à l'exception du mot-clé utilisé :

Table des matières

Syntaxe

struct attr-spec-seq  (optionnel) name  (optionnel) { struct-declaration-list } (1)
struct attr-spec-seq  (optionnel) name (2)
1) Définition de struct : introduit le nouveau type struct name et définit sa signification
2) S'il est utilisé sur une ligne seule, comme dans struct name ; , déclare mais ne définit pas le struct name (voir la déclaration anticipée ci-dessous). Dans d'autres contextes, nomme le struct précédemment déclaré, et attr-spec-seq n'est pas autorisé.
name - le nom de la structure en cours de définition
struct-declaration-list - un nombre quelconque de déclarations de variables, déclarations de champs de bits , et déclarations static_assert . Les membres de type incomplet et les membres de type fonction ne sont pas autorisés (sauf pour le membre de tableau flexible décrit ci-dessous)
attr-spec-seq - (C23) liste optionnelle d' attributs , appliqués au type de structure

Explication

Dans un objet struct, les adresses de ses éléments (et les adresses des unités d'allocation des champs de bits) augmentent dans l'ordre dans lequel les membres ont été définis. Un pointeur vers une struct peut être converti en un pointeur vers son premier membre (ou, si le membre est un champ de bits, vers son unité d'allocation). De même, un pointeur vers le premier membre d'une struct peut être converti en un pointeur vers la struct englobante. Il peut y avoir un remplissage sans nom entre deux membres quelconques d'une struct ou après le dernier membre, mais pas avant le premier membre. La taille d'une struct est au moins aussi grande que la somme des tailles de ses membres.

Si une structure définit au moins un membre nommé, il est permis de déclarer en plus son dernier membre avec un type de tableau incomplet. Lorsqu'un élément du membre de tableau flexible est accédé (dans une expression utilisant les opérateurs . ou -> avec le nom du membre de tableau flexible comme opérande de droite), alors la structure se comporte comme si le membre tableau avait la taille maximale tenant dans la mémoire allouée pour cet objet. Si aucun stockage supplémentaire n'a été alloué, elle se comporte comme un tableau avec 1 élément, sauf que le comportement est indéfini si cet élément est accédé ou si un pointeur juste après cet élément est produit. L'initialisation et l'opérateur d'affectation ignorent le membre de tableau flexible. sizeof l'omet, mais peut avoir plus de remplissage final que l'omission ne le suggérerait. Les structures avec des membres de tableau flexible (ou les unions qui ont un membre structure récursif possible avec un membre de tableau flexible) ne peuvent pas apparaître comme éléments de tableau ou comme membres d'autres structures.

struct s { int n; double d[]; }; // s.d is a flexible array member
struct s t1 = { 0 };          // OK, d is as if double d[1], but UB to access
struct s t2 = { 1, { 4.2 } }; // error: initialization ignores flexible array
// if sizeof (double) == 8
struct s *s1 = malloc(sizeof (struct s) + 64); // as if d was double d[8]
struct s *s2 = malloc(sizeof (struct s) + 40); // as if d was double d[5]
s1 = malloc(sizeof (struct s) + 10); // now as if d was double d[1]. Two bytes excess.
double *dp = &(s1->d[0]);    // OK
*dp = 42;                    // OK
s1->d[1]++;                  // Undefined behavior. 2 excess bytes can't be accessed
                             // as double.
s2 = malloc(sizeof (struct s) + 6);  // same, but UB to access because 2 bytes are
                                     // missing to complete 1 double
dp = &(s2->d[0]);            // OK, can take address just fine
*dp = 42;                    // undefined behavior
*s1 = *s2; // only copies s.n, not any element of s.d
           // except those caught in sizeof (struct s)
(depuis C99)

Similaire à union, un membre sans nom d'une struct dont le type est une struct sans nom est appelé struct anonyme . Chaque membre d'une struct anonyme est considéré comme un membre de la struct ou union englobante, en conservant leur disposition structurelle. Ceci s'applique récursivement si la struct ou union englobante est également anonyme.

struct v
{
   union // anonymous union
   {
      struct { int i, j; }; // anonymous structure
      struct { long k, l; } w;
   };
   int m;
} v1;
v1.i = 2;   // valid
v1.k = 3;   // invalid: inner structure is not anonymous
v1.w.k = 5; // valid

Similaire à union, le comportement du programme est indéfini si struct est définie sans aucun membre nommé (y compris ceux obtenus via des structs ou unions imbriquées anonymes).

(depuis C11)

Déclaration anticipée

Une déclaration de la forme suivante

struct attr-spec-seq  (optionnel) name ;

masque toute signification précédemment déclarée pour le nom name dans l'espace de noms des balises et déclare name comme un nouveau nom de structure dans la portée actuelle, qui sera défini ultérieurement. Jusqu'à ce que la définition apparaisse, ce nom de structure a un type incomplet .

Cela permet aux structures de se référer mutuellement :

struct y;
struct x { struct y *p; /* ... */ };
struct y { struct x *q; /* ... */ };
**Note:** Le code C++ présenté dans les balises `
` n'a pas été traduit conformément aux instructions, car il s'agit de code source qui doit rester inchangé. La structure HTML et les attributs ont également été préservés.

Notez qu'un nouveau nom de struct peut également être introduit simplement en utilisant une étiquette de struct dans une autre déclaration, mais si une struct précédemment déclarée avec le même nom existe dans l'espace de noms name space des étiquettes, l'étiquette se référerait à ce nom

struct s* p = NULL; // l'étiquette nommant une structure inconnue la déclare
struct s { int a; }; // définition de la structure pointée par p
void g(void)
{
    struct s; // déclaration anticipée d'une nouvelle structure s locale
              // cela masque la structure s globale jusqu'à la fin de ce bloc
    struct s *p;  // pointeur vers la structure s locale
                  // sans la déclaration anticipée ci-dessus,
                  // cela pointerait vers la structure s de portée fichier
    struct s { char* p; }; // définition de la structure s locale
}

Mots-clés

struct

Notes

Voir l'initialisation de struct pour les règles concernant les initialiseurs des structs.

Parce que les membres de type incomplet ne sont pas autorisés, et qu'un type struct n'est pas complet avant la fin de la définition, une struct ne peut pas avoir un membre de son propre type. Un pointeur vers son propre type est autorisé, et est couramment utilisé pour implémenter des nœuds dans des listes chaînées ou des arbres.

Parce qu'une déclaration de struct n'établit pas scope , les types imbriqués, les énumérations et les énumérateurs introduits par les déclarations dans struct-declaration-list sont visibles dans la portée environnante où la struct est définie.

Exemple

#include <stddef.h>
#include <stdio.h>
int main(void)
{
    // Déclarer le type struct.
    struct car
    {
        char* make;
        int year;
    };
    // Déclarer et initialiser un objet d'un type struct précédemment déclaré.
    struct car c = {.year = 1923, .make = "Nash"};
    printf("1) Car: %d %s\n", c.year, c.make);
    // Déclarer un type struct, un objet de ce type et un pointeur vers celui-ci.
    struct spaceship
    {
        char* model;
        int max_speed;
    } ship = {"T-65 X-wing starfighter", 1050},
    *pship = &ship;
    printf("2) Spaceship: %s. Max speed: %d km/h\n\n", ship.model, ship.max_speed);
    // L'adresse augmente dans l'ordre de définition. Du remplissage peut être inséré.
    struct A { char a; double b; char c; };
    printf(
        "3) Offset of char a = %zu\n"
        "4) Offset of double b = %zu\n"
        "5) Offset of char c = %zu\n"
        "6) Size of struct A = %zu\n\n",
        offsetof(struct A, a),
        offsetof(struct A, b),
        offsetof(struct A, c),
        sizeof(struct A)
    );
    struct B { char a; char b; double c; };
    printf(
        "7) Offset of char a = %zu\n"
        "8) Offset of char b = %zu\n"
        "9) Offset of double c = %zu\n"
        "A) Size of struct B = %zu\n\n",
        offsetof(struct B, a),
        offsetof(struct B, b),
        offsetof(struct B, c),
        sizeof(struct B)
    );
    // Un pointeur vers une struct peut être converti en pointeur
    // vers son premier membre et vice versa.
    char** pmodel = (char **)pship;
    printf("B) %s\n", *pmodel);
    pship = (struct spaceship *)pmodel;
}

Sortie possible :

1) Car: 1923 Nash
2) Spaceship: T-65 X-wing starfighter. Max speed: 1050 km/h
3) Offset of char a = 0
4) Offset of double b = 8
5) Offset of char c = 16
6) Size of struct A = 24
7) Offset of char a = 0
8) Offset of char b = 1
9) Offset of double c = 8
A) Size of struct B = 16
B) T-65 X-wing starfighter

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C publiées antérieurement.

DR Applicable à Comportement publié Comportement corrigé
DR 499 C11 les membres des structures/unions anonymes étaient considérés comme membres de la structure/union englobante ils conservent leur disposition en mémoire

Références

  • Norme C23 (ISO/IEC 9899:2024) :
  • 6.7.2.1 Spécificateurs de structure et d'union (p: TBD)
  • Norme C17 (ISO/CEI 9899:2018) :
  • 6.7.2.1 Spécificateurs de structure et d'union (p: 81-84)
  • Norme C11 (ISO/IEC 9899:2011):
  • 6.7.2.1 Spécificateurs de structure et d'union (p: 112-117)
  • Norme C99 (ISO/CEI 9899:1999) :
  • 6.7.2.1 Spécificateurs de structure et d'union (p: 101-104)
  • Norme C89/C90 (ISO/CEI 9899:1990) :
  • 3.5.2.1 Spécificateurs de structure et d'union

Voir aussi

Documentation C++ pour Déclaration de classe