structure

spécificateur_de_struct_ou_union ::=

struct_ou_union identificateuropt {liste_de déclarations-de-struct}

struct_ou_union identificateuropt

struct_ou_union ::=

struct

union

liste_de déclarations-de-struct ::=

declaration_de_struct

liste_de déclarations-de-struct declaration_de_struct

declaration_de_struct ::=

liste_de_qualificatifs_de_specificateurs liste_de_déclarateurs_de_struct ; /* le ; sépare les champs */

liste_de_qualificatifs_de_specificateurs ::=

qualificateur_de_type liste_de_qualificatifs_de_specificateursopt /* champ constant*/

spécificateur_de_type liste_de_qualificatifs_de_specificateursopt  /* int, ...*/

liste_de_déclarateurs_de_struct ::=   /* int i,j */

déclarateur_de_struct

liste_de_déclarateurs_de_struct , déclarateur_de_struct

déclarateur_de_struct ::=  /* pas d'initilisation des champs */

déclarateur        /* cas habituel*/

déclarateur : expression constante ;    /* pour les champs de bits*/

Déclarations et utilisations des structures

Un type structure (appelé record dans certains langages) regroupe un ou plusieurs champs qui peuvent être de types différents.

Dans une banque, par exemple, un compte est caractérisé par un numéro, le nom du client, son prénom, son adresse, etc. L’adresse, elle-même, est composée de plusieurs éléments de nature différente.

Exemples de déclarations de structure :

struct adr {

int numero;

char rue [16] ;

long codepostal ;

char ville [15] ;

}

struct compte {

long numero;

char nom [15] ;

char prenom [18] ;

struct adr adr1;      /* un champ structure */

} compte1 ;

struct adr adr1;

struct compte compte2 ; 

Dans ce dernier exemple, struct compte, définit un nouveau type, et compte1 et compte2 sont des variables de ce type.

Restrictions

La seule restriction sur le type des champs est qu’un champ ne peut pas être de type fonction, mais le type pointeur sur fonction est autorisé. Les champs ne peuvent pas comporter d’initialisations.

Initialisation des structures

Une structure peut être initialisée (avant la norme ANSI, on ne pouvait initialiser que des objets structures, externes ou statiques), soit à partir d’une autres structure de même type, soit en utilisant un agrégat. Les champs manquants sont initialisés à 0.

struct famille {

char nom [10] ;

char prenompere [10] ;

char prenompere [10] ;

int nbrenfants ;

} fam1 = {"Ogor", "Robert", "Marie-Francoise", 4} ; /* initialisation par un agrégat */

struct famille  fam2 = fam1 ; /* initialisation par copie d'un autre objet */

struct famille  fam3 = {"Rannou", "Robert", "Marie-Francoise", 4} ; /* initialisation par un agrégat */

Accès aux champs d’une structure

L’opérateur . permet de d’accéder à un champ d’un objet structure.

struct famille  fam3 ;

fam3.nom = "Kerdraon" ;

fam3.nbrenfant = 4 ;

struct compte compte1 ;

compte1.adresse.codepostal = 29200 ; /* en Français, de lit de droite à gauche */

L’opérateur -> permet d’accéder à un champ d’un objet structure, via un pointeur sur l’objet.

struct compte * ptcompte = & compte1 ; /* déclaration d'un piointeur qui pointe sur le compte compte1. On utilise l'opérateur & : adresse de  */

ptcompte -> adresse.codepostal = 35100 ; /* on modifie l'adresse postale de l'adresse du compte pointé par ptcompte */

Ces deux lignes sont équivalentes  :

(*ptcompte). numero = compte2.numero ; /* (*ptcompte) : l'objet pointé par ptcompte */ 

ptcompte -> numero = compte2.numero ;

Exemple de représentation de type complexe par un type structure 

 

Les opérations sur les structures

Il n’y a que trois opérations autorisées sur les structures :

  • Obtenir l’adresse d’une structure avec l’opérateur &
  • Accéder à un champ d’une structure avec les opérateurs . et ->
  • Affecter une structure à une autre (c'est bien une affectation de valeurs structure, pas d'adresse).

Exemple de passage de structures en paramètres à des fonctions

On demande d'écrire un programme qui lit les données (nom, âge et taille) concernant 2 personnes, puis compare leur âge (Dupont est plus vieux que Dupond), et fianlement leur taille (Dupont et Dupond ont la même taille.

char nom [20] ;

int age ;

float taille ;

} personne ;

void lecturedonneespersonnes (personne *p) {

/* En paramètre, un pointeur sur un object struct personne */

scanf ("%s %d %f", p -> nom, p -> age, p-> taille) ;

}

void imprimercomparaisondesages (personne p1, p2) {

if (p1.age > p2.age)

printf ("%s est plus vieux que %s \n", p1.nom, p2.nom) ;

else if (p2.age > p1.age)

printf ("%s est plus vieux que %s \n", p2.nom, p1.nom) ;

else

printf ("%s et %s ont le même age \n", p1.nom, p2.nom) ;

}

void imprimercomparaisondestailles (personne p1, p2) {

if (p1.taille > p2.taille)

printf ("%s est plus grand que %s \n", p1.nom, p2.nom) ;

else if (p2.taille > p1.taille)

printf ("%s est plus grand que %s \n", p2.nom, p1.nom) ;

else

printf ("%s et %s ont la même taille \n", p1.nom, p2.nom) ;

}

main () {

personne p1, p2 ;

lecturedonneespersonnes (&p1) ;

lecturedonneespersonnes (&p2) ;

imprimercomparaisondesages (p1, p2) ;

imprimercomparaisondestailles (p1, p2) ;

}

typedef struct {

 

Exemples de tableaux de structures

On lit les informations (nom, âge et taille) concernant 10  personnes et on les stocke dans un tableau.

On imprime le nom, l'age et la taille du plus grand, puis le nom, l'age et la taille du plus grand

typedef struct {

char nom [20] ;

int age ;

float taille ;

} personne ;

void liredonneessur10personnes (int *groupe) {

printf("Entrez 10 enregistrements de personnes");

printf ("chacun avec :  \n son nom, son age, sa taille \n);

for (int i =0 ; i<10, i++)

scanf ("s %d %f", p-> nom, p->age, p-> taille) ;

}

void imprimerplusvieux (* groupe) {  /* Pointeur sur le premier élément du tableau */

personne * leplusvieux = groupe ;        /*

personne * courant  = leplusvieux +1 ; /* Pointe sur le 2ème */

for (int i = 1 ; i < 10 ; i ++) {

if (courant ->age > leplusvieux -> age )

leplusvieux = courant ;

}

}

main () {

personne groupe [10] ;

liredonneessur10personnes (groupe) ;

imprimerplusvieux (groupe) ;

imprimerplusgrand (groupe);

}

Représentation mémoire

Soit la définition suivante d’un variable de type struct :

struct {int nombre ; int taille ; int compteur} ma_structure ;

Soit dim la taille la taille des entiers, en général 16 bits ou 32 bits.

Les différents champs sont placés de manière consécutive dans la mémoire dans l’ordre de leurs déclarations.

Ce type d’implémentation ne pose pas de réels problèmes. Par contre, il n’en est pas de même si on déclare des champs avec des contraintes d’alignement différents.

Exemple :

struct {

char le_vecteur [5] ;

int compteur ;

} ma_structure ;

Le champ le_vecteur utilise 5 octets. Mais du fait de la contrainte d’alignement pour les entiers, il n’est pas possible de faire démarrer l’objet compteur au 6ème octet. 3 octets du deuxième mot ne sont pas utilisés.

L’implémentation de la structure précédente est :

Soit dim (32 bits) la taille des entiers :

L’existence de ces octets de bourrage explique en partie que la norme interdit le test d’égalité entre structures.

En partie seulement, car le compilateur aurait pu utiliser des instructions  manipulant efficacement des blocs mémoire quand il n’y a pas d’octets de bourrage, et sinon effectuer des comparaisons champ par champ.

Les structures récursives (mise en œuvre de listes, …)

 

» Glossaire du langage C