Les structures
Une structure est une structure de données qui permet de stocker plusieurs valeurs de type différents dans une seule variable.
Les composants qui stockent ces différentes valeurs sont appelés champs et en utilisant l'opérateur . (point) et leur nom, il est possible de spécifier quelle
valeur on souhaite utiliser.
Voici un exemple de définition et d'utilisation d'une structure en C :
Comme vous pouvez le voir, la structure etudiant contient quatre champs : prenom, nom, section et resultat.
Chacun de ces champs doit être initialisé avant d'être utilisé sinon cela peut provoquer des comportements indéfinis.
Il est possible d'accéder à chacun de ces champs en utilisant l'opérateur . (point) suivi du nom du champ.
Dans le cas ci-dessus, l'initialisation des champs est un peu complexe car elle contient des chaines de caractères et il faut donc d'abord déclarer la variable avant d'initialiser chaque
champs avec les appels à malloc et strcpy. Cependant, pour les structures plus simples, il est possible d'initialiser les champs directement, comme dans l'exemple suivant :
#include <stdio.h>
struct point {
int x;
int y;
};
int main(void) {
// Déclaration et initialisation de la structure
struct point point = {.x = 10, .y = 20};
// Utilisation de la structure
printf("Point coordinates: (%d, %d)\n", point.x, point.y);
return 0;
}
Ici, la structure point est déclarée avec deux champs x et y, et elle est initialisée directement lors de sa déclaration avec la syntaxe {.x = 10, .y = 20}
qui assigne les valeurs 10 et 20 aux champs x et y respectivement. Notez qu'il est important de bien mettre des . (points) avant les noms des champs lors de l'initialisation.
Il est aussi possible de créer un tableau de structures en déclarant un tableau de la structure souhaitée, comme dans l'exemple suivant :
#include <stdio.h>
struct point {
int x;
int y;
};
int main(void) {
// Déclaration et initialisation
struct point points[3] = {
{.x = 10, .y = 20},
{.x = 30, .y = 40},
{.x = 50, .y = 60}
};
// Utilisation
for (int i = 0; i < 3; i++) {
printf("Point coordinates: (%d, %d)\n", points[i].x, points[i].y);
}
return 0;
}
Ici, un tableau de trois structures point est déclaré et initialisé avec des valeurs pour chaque champ de chaque structure.
Les structures allouées dynamiquement
Comme pour les tableaux, on a parfois besoin de créer dynamiquement une nouvelle structure. Pour cela, on utilise aussi les fonctions malloc et free :
Comme vous pouvez le voir, la structure point est allouée dynamiquement avec un appel à malloc et un pointeur vers la structure est retourné.
Pour accéder aux champs de la structure pointée, on peut utiliser soit l'opérateur * suivi de l'opérateur . (point) ou
l'opérateur -> qui combine les deux opérations en une seule.
Il est aussi possible de créer un tableau de structures allouées dynamiquement en multipliant la taille de la structure par le nombre d'éléments souhaités lors de l'appel à malloc :
Les énumérations
Les énumérations sont un autre type de structure de données qui permet de définir un ensemble de valeurs symboliques. Elles sont souvent utilisées pour représenter des états ou des options dans un programme. Cela permet d'éviter de traiter certaines valeurs numériques arbitraires avec une sémantique particulière et donc de rendre le code plus lisible et plus maintenable.
Voici un exemple d'énumération en C :
#include <stdio.h>
enum jour {
LUNDI,
MARDI,
MERCREDI,
JEUDI,
VENDREDI,
SAMEDI,
DIMANCHE
};
int main(void) {
enum jour aujourd_hui = MARDI;
if (aujourd_hui == MARDI) {
printf("Aujourd'hui, c'est mardi !\n");
}
return 0;
}
Il est aussi possible d'assigner des valeurs spécifiques aux éléments de l'énumération, comme dans l'exemple suivant :
#include <stdio.h>
enum HttpStatus {
HTTP_OK = 200,
HTTP_BAD_REQUEST = 400,
HTTP_UNAUTHORIZED = 401,
HTTP_FORBIDDEN = 403,
HTTP_NOT_FOUND = 404,
HTTP_INTERNAL_SERVER_ERROR = 500
};
int main(void) {
enum HttpStatus status = HTTP_NOT_FOUND;
if (status == HTTP_NOT_FOUND) {
printf("Erreur 404 : Page non trouvée.\n");
}
return 0;
}
Comportement des structures avec les fonctions
Contrairement aux tableaux, les structures ont un comportement relativement intuitif lorsqu'elles sont utilisées avec des fonctions.
Passage par valeur par défaut
Par défaut, une copie complète de la structure est créée lorsqu'elle est passée en argument à une fonction. Cela signifie que les modifications apportées à la structure dans la fonction n'affectentent pas la structure originale.
Voici un exemple illustrant ce comportement :
Cependant, il est possible de passer une structure par référence en utilisant un pointeur vers la structure, comme dans l'exemple suivant :
Retour d'une structure depuis une fonction
Contrairement aux tableaux, il est possible de retourner une structure depuis une fonction. Cela crée une copie complète de la structure retournée, comme dans l'exemple suivant :
Bonnes pratiques
Allocation dynamique que lorsque nécéssaire
Comme pour les tableaux, il est recommandé de n'utiliser l'allocation dynamique des structures que lorsque c'est nécéssaire.
Passage par référence pour les grosses structures
Si une structure est grosse (plus de quelques dizaines d'octets), il est généralement recommandé de la passer par référence aux fonctions en utilisant un pointeur pour éviter les copies coûteuses en performance du passage par valeur.
Utilisation de typedef pour simplifier le code
Dans les exemples précédents, les structures sont déclarées avec le mot-clé struct à chaque fois qu'elles sont utilisées.
Cela peut rendre le code verbeux et difficile à lire. Pour simplifier cela, il est possible d'utiliser le mot-clé typedef
pour créer un alias pour la structure, comme dans l'exemple suivant :
#include <stdio.h>
struct point {
int x;
int y;
};
typedef struct point point_t;
int main(void) {
// Déclaration et initialisation de la structure
point_t point = {.x = 10, .y = 20};
// Utilisation de la structure
printf("Point coordinates: (%d, %d)\n", point.x, point.y);
return 0;
}
Cela permet d'utiliser point_t comme type de données au lieu de struct point, ce qui rend le code plus concis et lisible.
Puisque cela est une pratique courante en C, il est possible de faire cela directement lors de la déclaration de la structure en combinant les deux opérations, comme dans l'exemple suivant :
#include <stdio.h>
typedef struct point {
int x;
int y;
} point_t;
int main(void) {
// Déclaration et initialisation de la structure
point_t point = {.x = 10, .y = 20};
// Utilisation de la structure
printf("Point coordinates: (%d, %d)\n", point.x, point.y);
return 0;
}
Parfois, on n'a pas envie de nommer la structure en elle-même car on a juste besoin de définir un nouveau type. Cela est possible en omettant le nom de la structure lors de la déclaration et en définissant seulement un nouveau type, comme dans l'exemple suivant :
#include <stdio.h>
typedef struct {
int x;
int y;
} point_t;
int main(void) {
// Déclaration et initialisation de la structure
point_t point = {.x = 10, .y = 20};
// Utilisation de la structure
printf("Point coordinates: (%d, %d)\n", point.x, point.y);
return 0;
}
Cela crée un type point_t sans nommer la structure sous-jacente.
Nom des structures et des typedef en minuscules
Il est recommandé de nommer les structures avec des noms explicites en minuscules et
de nommer les typedef avec les mêmes noms mais avec un suffixe _t pour indiquer qu'il s'agit d'un type défini par l'utilisateur.
INGInious