🧱 Le cours : créer ses propres types
Un tableau regroupe des valeurs du même type. Une structure regroupe des valeurs de types différents qui décrivent un même objet : un élève (nom, âge, moyenne), un point (x, y), une date (jour, mois, année). C'est l'ancêtre des classes de la programmation orientée objet, sans les méthodes.
Définir une structure
On déclare les champs entre accolades, et on termine par un point-virgule. Le type s'appelle alors struct Point. Contrairement à un tableau, une structure se copie avec = : tous les champs sont recopiés.
#include <stdio.h>
struct Point {
int x;
int y;
};
int main(void) {
struct Point a = {3, 4};
struct Point b = a; /* copie champ par champ */
b.x = 10;
printf("a = (%d, %d), b = (%d, %d)\n", a.x, a.y, b.x, b.y);
return 0;
}
Affichagea = (3, 4), b = (10, 4)
typedef : un nom plus court
Écrire struct Point à chaque fois est lourd. typedef donne un nom au type : après typedef struct { … } Point;, on écrit simplement Point p;. Par convention, les noms de types commencent par une majuscule.
Accéder aux champs : . et ->
Avec une variable de type structure, on utilise le point : e.age. Avec un pointeur vers une structure, on utilise la flèche : p->age, qui est un raccourci de (*p).age. C'est ce qui permet à une fonction de modifier la structure de l'appelant.
#include <stdio.h>
typedef struct {
char nom[20];
int age;
} Eleve;
void anniversaire(Eleve *e) {
e->age = e->age + 1; /* (*e).age = (*e).age + 1 */
}
int main(void) {
Eleve e = {"Lina", 17};
anniversaire(&e);
printf("%s a maintenant %d ans\n", e.nom, e.age);
return 0;
}
AffichageLina a maintenant 18 ans
Renvoyer une structure
Une fonction peut renvoyer une structure entière : elle est copiée vers l'appelant. C'est pratique pour renvoyer plusieurs valeurs d'un coup, sans passer par des pointeurs.
#include <stdio.h>
typedef struct {
int x;
int y;
} Point;
Point milieu(Point a, Point b) {
Point m = {(a.x + b.x) / 2, (a.y + b.y) / 2};
return m;
}
int main(void) {
Point a = {0, 0}, b = {6, 10};
Point m = milieu(a, b);
printf("milieu = (%d, %d)\n", m.x, m.y);
return 0;
}
Structures imbriquées et tableaux de structures
Un champ peut lui-même être une structure ou un tableau. On enchaîne alors les accès : segment.debut.x, classe[2].nom[0]. Un tableau de structures (voir la deuxième carte) est l'équivalent d'un tableau d'objets : on le parcourt avec une boucle, comme n'importe quel tableau.
typedef struct { int x; int y; } Point;
typedef struct {
Point debut;
Point fin;
} Segment;
Segment s = {{0, 0}, {4, 3}};
/* s.fin.x vaut 4, s.debut.y vaut 0 */
Énumérations : des noms pour des entiers
enum associe des noms aux entiers 0, 1, 2… Le code devient plus lisible qu'avec des nombres « magiques » : on écrit MARDI au lieu de 1.
#include <stdio.h>
enum Jour { LUNDI, MARDI, MERCREDI };
int main(void) {
enum Jour j = MARDI;
printf("%d\n", j);
if (j == MARDI) {
printf("c'est mardi\n");
}
return 0;
}
Aperçu : une structure qui se référence elle-même
Une structure peut contenir un pointeur vers une structure du même type. C'est la base des listes chaînées et des arbres : chaque maillon connaît le suivant. Ces structures s'allouent avec malloc (voir Mémoire dynamique).
struct Noeud {
int valeur;
struct Noeud *suivant; /* NULL pour le dernier maillon */
};