Approfondir : Structures et enregistrements
Une structure rassemble des champs de types éventuellement différents sous un même nom. Elle permet de représenter une entité cohérente : un étudiant possède par exemple un nom et une note, qui ne doivent pas être maintenus dans deux séries indépendantes.
Définir un type composé
struct Etudiant { char nom[32]; double note; }; définit la forme d’un étudiant. Le point-virgule après l’accolade est nécessaire. La définition du type ne crée pas à elle seule un étudiant ; il faut ensuite déclarer une variable de ce type. Une initialisation par accolades fournit les valeurs des champs dans l’ordre ou avec des désignateurs explicites.
Accéder aux champs
Avec une variable e, écrire e.note. Avec un pointeur p vers une structure, écrire p->note, ce qui correspond à (*p).note. Le type du champ reste important : note est un double, donc son affichage utilise %f ; nom est un tableau contenant une chaîne valide, affichée avec %s. Le nom du champ ne remplace pas son type.
Copie et contenu
Une affectation entre deux structures du même type copie leurs valeurs, y compris les éléments des tableaux contenus directement dans la structure. Si une structure contient un pointeur, l’affectation copie en revanche la valeur du pointeur, pas une allocation indépendante de l’objet pointé. Le terme « copie de structure » n’implique donc pas toujours une copie profonde des données externes.
Fonctions et tableaux de structures
Une structure peut être passée par valeur pour fournir une copie à une fonction. Pour la modifier sur place, passer son adresse. Un tableau de structures représente plusieurs entités : classe[i].note accède à une note donnée. Il faut toujours vérifier les indices comme pour n’importe quel tableau.
Organisation mémoire et fichiers
La structure peut contenir du remplissage entre ses champs pour respecter l’alignement. sizeof(struct Etudiant) n’est pas obligatoirement la somme des tailles des champs. Écrire directement ses octets dans un fichier crée donc un format dépendant du compilateur et de la machine. Pour un échange portable, définir une représentation explicite, par exemple un fichier texte avec les champs dans un ordre convenu.
Structures : copier un étudiant
#include <stdio.h>
struct Etudiant {
char nom[32];
double note;
};
int main(void) {
struct Etudiant a = {"Ada", 14.0};
struct Etudiant b = a;
b.note = 17.0;
printf("%s : %.1f\n", a.nom, a.note);
printf("%s : %.1f\n", b.nom, b.note);
return 0;
}
Sortie écran : essai 1
La copie du champ note peut être modifiée indépendamment.
Ada : 14.0
Ada : 17.0
S’entraîner et vérifier
Exercice 1. Que signifie p->note ?
Voir la correctionMasquer la correction
C’est le champ note de la structure désignée par p. Le pointeur doit désigner une structure valide. L’écriture est équivalente à (*p).note.
Exercice 2. Peut-on comparer deux structures avec == pour comparer tous leurs champs ?
Voir la correctionMasquer la correction
Non. Il faut comparer les champs selon leur type, par exemple les notes avec une règle appropriée et les noms avec strcmp. Un memcmp des octets ne remplace pas une comparaison sémantique, notamment à cause du remplissage et des représentations.
Pratiquer avec le TD/TP 1 corrigé · Tous les exercices C.
Repère de lecture : les codes proposés utilisent des constructions disponibles en C11. Les règles du langage et de sa bibliothèque sont consultables dans les projets publics du WG14 (C11) et du WG14 (C23). Les exemples n’utilisent pas les nouveautés propres à C23.