Structures de données
De nombreux objets traités par les programmes ne peuvent pas être représentés à l’aide d’un seul nombre ou d’une chaîne, mais sont constitués naturellement de plusieurs informations : une date = un jour, un mois, une année ; un nombre complexe = une partie réelle, une partie imaginaire ; un tableau = l’adresse de son premier élément, sa taille physique et sa taille effective ; une sphère = un centre, un rayon ; une personne = un nom, un prénom ; une adresse = un numéro, une rue, une ville, un code postal; … Une information élémentaire dans un objet est appelée un champ. Pour accéder à l’information contenue dans un champ d’un objet composite, il suffit d’ajouter un point au nom de l’objet et de le faire suivre du nom du champ.
Structures simples
Voici un exemple de définition et d’utilisation de structures. Il porte sur les dates, constituées de 3 informations : nom de jour (dans la semaine), jour dans le mois, mois et année. typedef struct { /* ou typedef struct T_Date */ int jour, mois, annee; char lejour[9]; /* « lundi », …., « dimanche » */ } Date;
Date d1, d2; d1.jour = 24; d1.mois = 12; d1.annee = 1965; d2.annee = d1.annee+3; printf(« %d-%d-%d\n »,d1.jour,d1.mois,d1.annee);} Nous donnons deux versions de fonction de calcul de la veille d’une date. La première délivre un résultat Date (version recommandée, car sans pointeur), la deuxième modifie la date passée en paramètre (à éviter).
Date veille(Date d) { Date res = d; if (res.jour != 1) res.jour–; else if (res.mois != 1) { res.mois–; res.jour = nbJoursDansMois(res.mois,res.annee); } else { res.jour = 31; res.mois = 12; res.annee–; } return res; }
void veille(Date *d) { if ((*d).jour != 1) (*d).jour–; else if ((*d).mois != 1) { (*d).mois–; (*d).jour = nbJoursDansMois((*d).mois,(*d).annee); } else { (*d).jour = 31; (*d).mois = 12; (*d).annee–; } }
Page 5 Pierre Tellier
Remarque : (*d).jour est identique à d->jour. C’est d’ailleurs la notation que l’on rencontre le plus souvent. En revanche, l’écriture *f.adresse est équivalente à *(f.adresse).
Structures imbriquées
Exemple : fiche de renseignement Nous définissons un type FICHE, destiné à contenir des informations sur des personnes. Ces informations sont : nom, prénom, adresse, date de naissance, sachant qu’une adresse est constituée d’un numéro, d’un nom de rue, d’un code postal et d’une ville, alors que la date de naissance est la réunion de 3 informations : jour, mois et année. Nous définissons notre type ainsi : #include <stdio.h> #include <string.h> typedef struct t_date { int j, m; char a[3]; /* annee = 2 caractères + fin de chaîne = bug an 2000 🙂 */ } TDate;
typedef struct t_adresse { int numero, code; char *rue, *ville; } TAdresse;
typedef struct t_fiche { char *nom, *prenom; /* pointeurs sur nom et prenom */ TDate naissance; /* date de naissance */ TAdresse *adresse; /* pointeurs sur adresse => pas présente */ } TFiche; Nous écrivons ensuite les fonctions permettant d’initialiser une fiche. Pour réduire le risque d’erreurs, il est impératif de procéder comme dans l’exemple, c’est-à-dire de ne pas tenter d’accéder directement aux sous champs des champs, mais de passer par des objets intermédiaires qui n’ont qu’un seul niveau de décomposition et qui sont plus simples à initialiser. Certaines fonctions fonctionnent sur le mode des paramètres modifiables, ce qu’il vaut mieux éviter en principe.
void saisieAdresse(TAdresse *adr) { char chaine[1024]; int n;
printf(« Saisie de l’adresse\n »); printf(« Numero : « ); scanf(« %d », &n); adr->numero = n; printf(« Nom de la rue : « ); scanf(« %s », chaine); adr->rue = strdup(chaine); printf(« Code Postal : « );scanf(« %d »,&adr->code); /* ou adr.code (rare) */ printf(« Ville : « ); scanf(« %s », chaine); adr->ville = strdup(chaine);
return ; }
TDate saisieNaissance(void) { TDate d;
printf(« Saisie de la date de naissance\n »); printf(« Jour : « ); scanf(« %d », &d.j); printf(« Mois (1-12) : « ); scanf(« %d », &d.m);
printf(« Année (19XX) : « ); scanf(« %s », d.a);
return d; }
void saisieFiche(TFiche *f) { char chaine[1024]; TAdresse *adr; TDate date;
printf(« Nom : « ); scanf(« %s », chaine); f->nom = strdup(chaine); printf(« Prénom : « ); scanf(« %s », chaine); f->prenom = strdup(chaine);
adr=(Tadresse*)malloc(sizeof(TAdresse)); /*allouer une nouvelle adresse*/ /* il ne faudra pas la libérer */ f->adresse = adr; /* le champ adresse pointe sur la nouvelle adresse */ saisieAdresse(adr); /* plus facile que : scanf(« %d », &f->adresse->numero); !!! */
date = saisieNaissance(); f->naissance = date; /* recopie de la date f->naissance=saisieDate(); */ /* plus facile que : scanf(« %d », &f->naissance.j); !!! */ } Nous donnons enfin un exemple d’utilisation de ces fonctions : une fonction qui permet de visualiser le contenu de la structure, et un programme principal qui manipule ces fonctions. void visuAdresse(TAdresse adr) { printf(« %d rue %s, %d %s\n », adr.numero, adr.rue, adr.code, adr.ville); }
void visuDate(TDate d) { printf(« %d %d 19%s\n », d.j, f.naissance.m, f.naissance.a); }
void visuFiche(TFiche f) { TAdresse *adr; TDate d; adr = f.adresse; d = f.naissance; printf(« %s %s\n », f.nom, f.prenom); printf(« Adresse : « ); visuAdresse(*adr); printf(« Né(e) le : « ); visuDate(d); }
int main(int argc, char **argv) { TFiche fiche;
saisieFiche(&fiche); visuFiche(fiche); exit(0); }