Menu

Les structures en C : définition, initialisation et utilisation

Comment regrouper des valeurs liées en un seul type avec une structure C - la déclarer, accéder aux membres avec l'opérateur point, les initialiseurs désignés, les tableaux de structures, l'imbrication, et la règle de copie au passage qui surprend tout le monde.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Un tableau contient plusieurs valeurs d'un seul type. Une struct fait l'inverse : elle contient quelques valeurs de types différents et les traite comme une seule chose. Un point, c'est un x et un y. Un employé, c'est un nom, un identifiant et un salaire. Sans structures, vous trimballeriez cela comme trois variables éparses en espérant ne jamais mélanger le salaire de l'un avec celui de l'autre.

Déclarer un type structure

Une déclaration de structure nomme une étiquette et liste les membres entre accolades :

struct Point {
    int x;
    int y;
};

Le point-virgule après l'accolade fermante est obligatoire - l'oublier est l'un des messages d'erreur les plus déroutants qu'un débutant en C rencontrera, car le compilateur accuse la ligne suivante.

Cela déclare un type, pas une variable. En C, le nom complet du type est struct Point, mot-clé compris :

Les membres se lisent et s'écrivent avec l'opérateur point, p.x. Une variable structure est une variable ordinaire : elle vit sur la pile, peut être affectée à une autre structure du même type avec =, et disparaît à la fin de sa portée.

Écrire struct Point partout devient fatigant, c'est pourquoi presque tout le vrai code C enveloppe la déclaration dans un typedef pour que le type soit simplement Point.

Initialiser une structure

Vous pouvez remplir une structure au moment où vous la déclarez. La forme positionnelle liste les valeurs dans l'ordre de déclaration :

struct Point p = {3, 4};   // x = 3, y = 4

L'initialiseur désigné nomme chaque membre à la place, ce qui est plus clair et plus sûr :

Trois choses à remarquer :

  • La forme désignée peut lister les membres dans n'importe quel ordre, donc ajouter ou réordonner un membre plus tard ne décale pas silencieusement les valeurs dans les mauvaises cases.
  • Tout membre omis est initialisé à zéro - {0} est la façon idiomatique de mettre à zéro une structure entière.
  • Un membre char name[32] est un vrai tableau dans la structure, donc la chaîne est stockée dans la structure elle-même, pas ailleurs. Cela signifie aussi que a.name = "Ada"; après coup est illégal ; il vous faut strcpy (voir fonctions de chaînes).

Une structure sans initialiseur n'est pas initialisée, et lire ses membres avant de les écrire est un comportement indéfini. = {0} ne coûte rien et supprime toute la catégorie de bug.

Affecter et comparer

L'affectation de structure copie chaque membre :

a est intacte : b a reçu sa propre copie. Mais notez ce que le C ne vous donne pas - il n'y a pas de == pour les structures. if (a == b) ne compilera pas. Comparer signifie comparer les membres :

if (a.x == b.x && a.y == b.y) { /* egales */ }

Ne prenez pas memcmp comme raccourci. Les compilateurs insèrent des octets de remplissage invisibles entre les membres pour l'alignement, et ces octets peuvent contenir du garbage, donc deux structures aux membres identiques peuvent se comparer différentes octet par octet.

Les tableaux de structures

Parce qu'une structure est un type comme un autre, vous pouvez en faire un tableau - la façon standard de contenir une table d'enregistrements.

staff[i].salary se lit « élément i, puis son membre salary » - l'indexation s'attache plus fort que le point, aucune parenthèse n'est donc nécessaire.

Les structures sont copiées dans les fonctions

C'est la règle qui piège les gens. Quand vous passez une structure à une fonction, le C la copie. La fonction travaille sur sa propre copie privée :

p vaut toujours (3, 4). Lire une structure par valeur, comme le fait distance_from_origin, est parfaitement acceptable et souvent le choix le plus clair pour de petites structures. Mais si la fonction doit changer la structure de l'appelant - ou si la structure est assez grande pour que la copier soit du gaspillage - passez plutôt un pointeur. C'est le sujet de structures et pointeurs.

Renvoyer une structure par valeur fonctionne de la même façon et est parfaitement légal :

struct Point make_point(int x, int y) {
    struct Point p = {x, y};
    return p;
}

Les structures imbriquées

Un membre de structure peut lui-même être une structure. Enchaînez les points pour aller à l'intérieur :

b.published.year descend d'un niveau à la fois. L'imbrication est la façon de construire de vrais modèles de données en C - et la structure interne est stockée à l'intérieur de l'externe, pas pointée, donc tout le Book est un bloc de mémoire contigu.

Taille, remplissage et ordre des membres

sizeof vous dit la taille d'une structure, et la réponse est souvent plus grande que la somme de ses membres :

Sur une machine typique, Wasteful fait 12 octets et Tidy en fait 8, alors que les deux contiennent les mêmes trois membres. Le compilateur insère du remplissage pour que chaque membre soit à une adresse qui plaît à son type - un int veut généralement un multiple de 4. Grouper les membres les plus grands avant les plus petits tend à mieux compacter.

Cela compte rarement (un tableau d'un million d'éléments, la disposition d'un paquet réseau) et jamais pour la justesse de votre propre code. Ce que cela explique, c'est pourquoi sizeof vous surprend, et pourquoi memcmp sur des structures est une mauvaise idée.

Questions fréquentes

Comment déclare-t-on une structure en C ?

Écrivez struct suivi d'un nom d'étiquette et d'une liste de membres entre accolades : struct Point { int x; int y; };. Cela déclare un type, pas une variable. Pour créer une variable, vous écrivez ensuite struct Point p; - le mot struct fait partie du nom du type en C, c'est pourquoi la plupart du code l'associe à un typedef.

Quelle est la différence entre . et -> pour les structures ?

Utilisez . quand vous avez la structure elle-même (p.x) et -> quand vous avez un pointeur dessus (ptr->x). La flèche est un raccourci pour (*ptr).x. Voir structures et pointeurs pour les détails.

Les structures sont-elles passées par valeur ou par référence en C ?

Par valeur, toujours. Passer une structure à une fonction copie chaque membre, donc les changements dans la fonction n'affectent pas la variable de l'appelant. Pour modifier l'original - ou pour éviter de copier une grosse structure - passez plutôt un pointeur.

Comment initialiser une structure en C ?

Soit par position, struct Point p = {3, 4};, soit avec des initialiseurs désignés, struct Point p = {.y = 4, .x = 3};. La forme désignée nomme chaque membre, elle survit donc à un réordonnancement ultérieur de la structure et laisse à zéro les membres non nommés.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER