V. Structures, unions et énumérations▲
V-A. Les structures▲
Une structure est une variable qui regroupe une ou plusieurs variables, non nécessairement de même type (contrairement aux tableaux), appelées champs. L'accès à un champ d'une structure se fait par l'intermédiaire de l'opérateur point. Par exemple :
struct
{
char
nom[20
]; int
age;}
marc, luc, jean;
...
strcpy
(
marc.nom, "
Marc
"
);
marc.age =
24
;
...
D'habitude on renomme tout d'abord une structure avant de l'utiliser. Par exemple :
typedef
struct
{
char
nom[20
]; int
age;}
PERSONNE;
...
PERSONNE marc, luc, jean;
...
Une structure peut être également nommée. Par exemple :
struct
personne_s {
char
nom[20
]; int
age;}
marc, luc, jean;
Dans ce cas, marc, luc et jean sont de type struct personne_s. On aurait également pu écrire :
struct
personne_s {
char
nom[20
];
int
age;
}
;
Puis :
struct
personne_s marc, luc, jean;
Et bien entendu, du moment qu'une structure a été nommée, on peut très bien écrire :
typedef
struct
personne_s PERSONNE;
L'opérateur = (affectation) peut être utilisé avec des structures (pour copier une structure), mais dans la pratique on n'en a rarement besoin. En effet, puisque l'espace occupée en mémoire par une structure peut être gigantesque, copier une structure vers une autre pénalisera considérablement, non seulement en termes de performances, mais aussi en termes de vitesse d'exécution (à cause du temps d'accès à une cellule mémoire). C'est pourquoi on utilise généralement des pointeurs pour manipuler les structures. L'opérateur -> permet d'accéder à un champ d'une structure via un pointeur. Par exemple :
#include <stdio.h>
#include <string.h>
struct
personne_s {
char
nom[20
];
int
age;
}
;
void
affiche_personne
(
struct
personne_s *
p);
int
main
(
)
{
struct
personne_s jean;
strcpy
(
jean.nom, "
Jean
"
);
jean.age =
24
;
affiche_personne
(&
jean);
return
0
;
}
void
affiche_personne
(
struct
personne_s *
p)
{
printf
(
"
Nom : %s
\n
"
, p->
nom);
printf
(
"
Age : %d
\n
"
, p->
age);
}
L'initialisation d'une structure se fait de la même manière que pour un tableau, avec les mêmes règles. Par exemple :
struct
personne_s jean =
{
"
Jean
"
, 24
}
;
V-B. Les unions▲
Une union est, en première approximation, une variable dont le type n'est pas figé (c'est-à-dire une variable « caméléon »). En fait, une variable de type union est une variable composée de champs de types différents et non nécessairement de même taille, mais à la différence d'une structure, les champs d'une union partagent le même emplacement mémoire. Les unions permettent donc de faire une économie de mémoire. Du point de vue syntaxique, elles ressemblent beaucoup aux structures, il suffit de remplacer struct par union.
Le programme suivant montre un exemple d'utilisation de cette union, u, entre trois variables de types différents (n, x et p).
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
union
trois_en_un {
int
n;
double
x;
char
*
p;
}
;
int
main
(
)
{
union
trois_en_un u;
printf
(
"
Entrez un entier :
"
);
scanf
(
"
%d
"
, &(
u.n));
printf
(
"
Vous avez entre : %d
\n\n
"
, u.n);
printf
(
"
Entrez un nombre flottant :
"
);
scanf
(
"
%lf
"
, &(
u.x));
printf
(
"
Vous avez entre : %f
\n\n
"
, u.x);
u.p =
malloc
(
20
);
if
(
u.p !=
NULL
)
{
strcpy
(
u.p, "
Au revoir.
"
);
printf
(
"
%s
\n\n
"
, u.p);
free
(
u.p);
}
return
0
;
}
Puisque les champs d'une union utilisent le même espace mémoire, les unions sont également utilisées pour interpréter un même motif binaire dans différents contextes (par exemple pour interpréter le contenu d'une mémoire comme un flottant, ou comme un entier…), mais cette pratique est généralement utilisée dans les programmes de très bas niveau plutôt que dans des programmes portables.
V-C. Les champs de bits▲
Le langage C permet de créer, à l'intérieur d'une structure, des données (champs) dont la taille est spécifiée en bits. Ces champs doivent être de type entier. Par exemple :
struct
{
unsigned
a : 1
;
unsigned
b : 1
;
unsigned
c : 4
;
unsigned
d : 3
;
}
x;
Ici, on a une structure x composée de 4 champs tous de type unsigned int : a de taille = 1 bit, b de taille = 1 bit, c de taille = 4 bits, et d de taille = 3 bits. Cela ne signifie pas que la taille de x est de 9 bits, mais au moins 9 bits. La taille d'une donnée est toujours un multiple entier d'un « octet ».
Les champs de bits peuvent être également utilisés, en faisant une union avec un autre type, dans des programmes dépendant d'une plateforme particulière pour accéder directement aux bits d'une donnée quelconque. On retrouve alors l'intérêt de créer des champs sans nom : ils sont juste utilisés pour espacer des champs entre eux. Dans des programmes portables, on utilisera plutôt les opérateurs de manipulation de bits que les unions et les champs de bits.
Attention ! Puisque les champs d'un champ de bits représentent des bits d'une variable et non des variables indépendantes, on ne peut pas leur appliquer l'opérateur & (adresse de). Mais il peut bien entendu être appliqué à la structure elle-même qui est une variable comme toutes les autres.
V-D. Les énumérations▲
Une énumération est un sous-ensemble énuméré du type int. La déclaration d'une énumération ressemble un peu à celle d'une structure Par exemple :
typedef
enum
{
PION,
CAVALIER,
FOU,
TOUR,
REINE,
ROI
}
CLASSE;
...
CLASSE classe =
FOU; /* Ou egalement int classe = FOU; */
...
Qu'on aurait également pu écrire :
#define PION 0
#define CAVALIER PION + 1
#define FOU CAVALIER + 1
#define TOUR FOU + 1
#define REINE TOUR + 1
#define ROI REINE + 1
...
int
classe =
FOU;
...
L'opérateur = peut également être utilisé dans une déclaration d'un type énuméré pour assigner explicitement une valeur à une constante.