Index des CoursChapitre précedentChapitre 9

LES STRUCTURES ET LES UNIONS

 

Objectifs :

•  Connaître la syntaxe de la déclaration des structures en langage  C

•  comprendre ; l’utilisation d’une structure

  Connaître la déclaration, des types synonymes en C

•  Différencier entre  les structures  et les unions

Elément  de contenu :

•  Exemple de déclaration de structure

•  Utilisation d’une structure

  La déclaration de types synonymes :typedef

•  Exemples de structures

•  La portée du nom de model de structure

  Structure transmise en argument d’une fonction :

L’opérateur->

•  Les champs de bits

  Les unions

Nous avons déjà vu comment es tableaux permettaient de désignez sous un seul nom un ensemble de valeur de même type, chacune d’entre elles étant repérée par un indice.

La structure va nous permettre de désigner, sous un seul nom, un ensemble de valeurs pouvant être de types différents. L’accès à chaque élément de la structure (nommé champ) sera, cette fois, non plus par une indication de position, mais par son non, au sein de la structure.

Ce chapitre est consacré à l’étude des structures. Nous y traiterons également les  unions dont la syntaxe de déclaration est très proche.

I-EXEMPLE DE DECLARATION DE STRUCTURES

Examinons cette déclaration :

Struct enreg

{        int numéro ;

          int qte ;

float prix ;

           } ;           

Celle-ci définit un modèle de structure mais ne réserve pas des variables correspondant à cette structure. ce modèle porte le nom de enreg et il précise le nom et le type de chacun des champs des la structure (numéro, qte et prix).

Une  fois un tel modèle défini, nous pouvons déclarer des variables du type correspondant. Par exemple :

Struct enreg art1 ;

Réserve un emplacement nommé artI ‘’ de type enreg” destiné a contenir deux entiers et un flottant (ce qui correspond à 8 octets).

De manière semblable :

Struct enreg art1 ,art2;

Réserverait deux emplacements art1 et art2 du type enreg.

Il  est également possible de regrouper la définition du modèle de la structure et a déclaration du type des variables dans une seule instruction comme dans cet exemple :

Struct enreg

{        int numéro ;

          int qte ;

float prix ;

           } art1,art2 ;

Dans ce cas, il est même possible d’omettre le non de modèle (enreg), à condition que l’on ait pas à déclarer par la suite des variables de ce type.

II- UTILISATION D’UNE STRUCTURE

En C, il est possible d’utiliser une structure de deux matières :

•    en travaillant individuellement sur chacun de ses champs,

•      en travaillant de manière globale sur l’ensemble dola structure.

II.1 Utilisation des champs d’une structure

Chaque champ d’une structure peut être manipulé  comme n’importe quelle variable du type correspondant. La désignation d’un champ se note on faisant suivre le nom de la variable structure du nom de champ tel qu’il a été défini dans le modèle (le nom de modèle lui même n’intervenant d’ailleurs pas). Les deux noms étant séparés par un opérateur de référence note

(.).

Voici quelque exemple utilisant le modèle enreg et les variables art1 et art2 déclaré de ce type :

artl.numéro= 15;

Affecte la valeur 15 au champ numéro de la structure art1.

printf (“%e, art1.prix);

Affiche, suivant le code format %e, la valeur du champ prix de la structure art1.

scanf (%e“, &art2.prix);

lit, suivant le code format %e, a valeur du champ prix de la structure art2.

           Art1.numéro++

Incrémente de1 la valeur du champ numéro  de la  structure art1.

Remarque:

L’opérateur de référence (.) a une priorité élevée, de sorte qu’aucune des expressions ci-dessus ne nécessite de parenthèses.

II.2 Utilisation globale d’une structure

il    est possible d’affecter à une structure le contenu d’une structure définie à partir du même modèle. Par exemple, si les structures art1 et art2 ont été déclarées suivant le modèle enreg, nous pourrons écrire :

art1 = art2;

Il   faut noter qu’une telle affectation n’est possible que si les structures ont été définies avec le même nom de modèle ; en particulier elle sera impossible avec des variables ayant une structure analogue mais définies sous deux noms différents.

L’opérateur d’affectation et, comme nous le verrons un peu plus loin, l’opérateur d’adresse &, sont les seuls opérateurs s’appliquant de manière  globale, à un structure.

Remarque:

L’affectation globale n’est pas possible entre tableau Elle l’est par contre, entre structures.

Ainsi, il est possible, en créant artificiellement une structure contenant un  seul champ qui est un

Tableau, de réaliser une affectation globale entre tableaux.

II.3 Initialisation des structures

On retrouve pour les structures les règles d’initialisation automatiques qui sont en vigueur pour

Tous les types de variables, a savoir :

•     Les structures possédant a classe ”statique” sont initialisées à zéro.

•     Les structures possédant la classe “automatique” ne sont pas initialisées et contiennent donc, à priori, des valeurs” aléatoire”

Par contre, en ce qui Concerne les possibilités d’initialisation explicite au sein d’un programme, seules les structures de classe statique peuvent être initiaLisées. Cette possibilité concerne donc, soit les structures déclarées à un niveau global (en dehors de toute fonction), soit les fonctions déclarées au sein de fonctions avec l’attribut static

Voici, un exemple d’initialisation de notre structure art1, au moment de sa définition :

Struct enreg art1= { 15,285,5.365};

Notez qu’on peut omettre certaines valeurs

III- LA DECLAR.ATION DE TYPES SYNONYMES :TYPEDEF

La déclaration typedef permet de définir et ce que l’on nomme en langage C des “types synonymes”. A priori, elle s’applique à tous les types et pas seulement aux structures. C’est pourquoi nous commencerons par l’introduire sur quelques exemples avant de montrer l’usage qu’on peut on faire avec les structures.

III Exemples d’utilisation de typedef

La déclaration :

Typedef int entier ;

Signifie que entier r est synonyme de int, de sorte que les déclarations suivantes sont équivalentes :

                   int n, p;              entier n,p;

De même

Typedef  int * ptr;

Signifie  que ptr  est synonyme de int*. Les déclarations suivantes sont équivalentes :

Int*n,*p ;                         ptr n,p ;

III.2 Application aux structures

En faisant usage de typedef, les déclarations de structure art1 et art2 du premier paragraphe  peuvent être réalisé ainsi :

Struct enreg

{   int numéro ;

     int qte ;

      float  prix ;

 } ;

typedef struct enreg s_enreg;

s_enreg art1,art2;

ou encore, plus simplement

typedef struct

{  int numéro ;

int qte;

} s_enreg;

s_enreg art1,art2;

L’emploi de typdef  permet  ainsi de déclarer  des variables du type correspondant  à noter modèle de structure enreg ,à l’aide d’un seul mot de notre choix (ici s_enreg). La deuxième forme montre comment le nom de modèle peut être omis , sans  compromettre d’éventuelles déclarations ultérieures de variable de type que l’on a ainsi définit.

IV- EXEMPLES DE STRUCTURES

Ici, nous allons  étudier des exemples de:

•     structures comportant  des tableaux

•     tableaux dont les éléments sont des structures

•     structures comportant d’autres structures

IV.1 Structures comportant des tableaux

Soit les déclarations suivantes :

struct person {

            char nom [30] ;

            char prénom[20] ;

            float heures[30] ;

           }employer

, courant

Celle-ci réserve les emplacements pour deux structures nommées emplye et courant. Ces dernières  comportent trois champs

- nom qui est un tableau de 30 caractères,

- prénom qui est un tableau de 20 caractères

- heures qui est  un tableau de 31 flottants.

On peut, par exemple, imaginer que ces structure permettent de conserver pour un employé d’une entreprise les informations suivantes :

              - nom

 -        prénom

              -  nombre d’heures de travail effectué pendant chaque jour du mois courant

La notation :

Employe.heures[5]

Désigne le cinquième élément du tableau heures de la structure employe .il  s’agit dun élément. De type float

De même:

Employe.nom[1]

Représente le premier caractère de champ  nom de la structure employe.

par ailleurs

&courant. heures[5]

Représente l’adresse du cinquième  élément du tableau heures de la structure courant.

Enfin:

Courant.nom

Représente le champ nom de la structure courant, c’est a dire l’adresse de ce tableau.

IV.2 Tableaux de structures

Examinons ces déclarations

Struct point {

Char nom ;

                      int   x ;

                      int   y ;

                     } ;

struct point courbe[50]

La structure point pourrait, par exemple, servir à représenter un point d’un plan  point qui serait défini par son nom (caractère) et ces deux coordonnés.

La structure courbe pourrait servir a représenter un ensemble de 50 points du type ainsi définit. Courbe est un tableau de 50 éléments du type point.

Si i est un entier, la notation :

Courbe [i].nom

Représente le nom du point de rang i du tableau courbe. Il s’agit donc d’une valeur de  type char. La notation :

courbe.nom[I]

N’a pas de ces dans cet exemple

De même, la notation

Courbe[i].x

Désigne la valeur du champ x de l’élément du rang i du tableau  courbe.

Par ailleurs

Courbe[4]

Représente la structure de type point correspondant au quatrième élément du tableau courbe

enfin combe est un identificateur de tableau et  désigne donc son adresse de début.

IV.3 Structures comportant d’autres structures

Supposons qu’a l‘intérieur de nos structure employe et courant définies précédemment nous

ayons besoin d’introduire deux dates : la date d’embauche et la date d ‘entrée dans le dernier poste occupé. Si ces dates sont elles mêmes des structure comportant trois champs correspondant au jour, au mois et à l‘année, nous pouvons alors procéder aux déclarations suivantes :

struct date{

                        int jour;

int mois;

int années ;

struct personne{

char nom[30]
char prénom[20]

float  heures [31

struct date date _entree

struct date date _poste

                       } employe, courant

Nous voyons que  la deuxième déclaration fait intervenir un modèle de structure (date) précédemment défini.

La notation

employe.date_embauche.annee

représenté l‘année d’embauche correspondant à la structure employe. Il s’agit d’une valeur de type int

courant. date _embauche

représente la date d’embauche correspondant à la structure courant Il s‘agit cette fois d’une structure de type date. Elle pourra éventuellement faire objet d’affectations globale comme dans :

courant.date_, embauche = employe. Date_ poste

V-LA PORTEE DU NOM DE MODELE DE STRUCTURE

La portée d’un modèle de structure dépend de l’emplacement de sa déclaration :

—        Si elle se situe au sein, d’une fonction (y compris la fonction main), elle n’est accessible que depuis cette fonction.

—        Si elle se situe ci dehors d’une fonction, elle est accessible de toute la partie du fichier source qui suit la déclaration.

Par contre, il n’est pas possible, dans un fichier source donné de faire référence à un modèle défini dans un autre source. Notez qu’il ne faut pas assimiler le nom de modèle d’une structure à un nom de variable ; en effet, il n’est pas possible, dans ce cas d’utiliser la déclaration extern.

Il est néanmoins toujours possible de placer un certain nombre de déclaration de modèle  de structures dans un fichier séparé que l’on incorpore par #include à tous les sources où l’on en a besoin. Cette méthode évite la duplication des déclarations identiques.

Le môme problème de portée se pose pour les synonymes définis par typedef . Les mêmes solutions Peuvent être  apportées par l’emploi de #include

VI- STRUCTURE TRANSMISE EN ARGUMENT D’UNE FONCTION:

L’OPERATEUR ->

Le langage C l’autorise pas la transmission des valeurs d’une structure, Par centre, il autorise la transmission de l’adresse à l‘aide de l’opérateur &.

Supposons, par exemple que l’on ait défini, à un niveau global, le modèle enreg comme suit :

struct enreg {

int a;

                       float b ;

}

Si x est une structure de modèle energ, un appel de fonction tel que :

fct (&x)

transmettra l’adresse de x à la fonction fct. Dans la mesure où le modèle energ a été défini a un niveau global, l’en-tête de la fonction pourra être de la forme :

fct (struct energ * p )

Mais le problème qui se pose est de savoir comment, au sein de cette fonction,fct accéder à chacun des champs de la structure, et ceci à partir de son adresse de début . En effet

I’opérateur (.) utilise comme opérande de gauche un identificateur de structure et non une adresse (contrairement a ce qui se passe pour un tableau)

On fait appel â un nouvel opérateur noté ->, lequel permet d’accéder aux différents champs d’une structure à partir de son adresse de début . Ainsi par exemple :

Désignera le second champ de la structure reçu en argument

Voici un programme illustrant l’emploi de cet opérateur

Struct energ {

int a;

f/oat b;

};

main()

{

Struct energ x;

Void fct (sî;uct enreg *);

x.a =1,

x.b =12.5;

fct(&x) ;

printf(‘’\non retour dans main : %d %c ‘’,x.a,x.b) ;

}

voîd fct(Struct energ *5)

{

printf (‘’\ndans fct : %d %c ‘’,x.a,x.b) ;

x-> a =0;

x->b =0;

}

dans fct:1 1.250000c + 001

au retour dons main : O 0. 0000000c+000

VII - LES CHAMPS 0E BITS

Nous avons déjà vu que le langage C disposait d’opérateur de bits. De plus ce langage permet de définir, au sein des structures, des variables occupant un nombre défini de bits (de 1à 6).

Cela peur s’avérer utile :

soit pour compacter l’information par exemple, un nombre entier compris entre 1et 15 pourra être rangé sur 4 bits au lieu de 16.

soit pour ’’décortiquer” le contenu d’un “motif binaire”, par exemple int “mot d’état” en provenance d’un périphérique spéciaLisé.

Examinons cet exemple de déclaration :
             struct état

                         {
                         unsigned         pert                   :1 ;

                         unsigned         ok                     :1;

int                   donnee 1          :5;

                        int                                            :3;

                       unsigned          ok2                   : 1;

                       int                   donnee 2           :4;

} ;

struct etat mot ;

La variable mot ainsi déclarée peut être schématisée ainsi :

15                                                                         8             

7

 
                               
 

Donne 2

Ok2

     

Donne 1

ok 1

pret

     0

Les indications figurant à la suite des ‘‘deux points ( :)” précisent la longueur du champ de bits Ceux-ci sont alloués ou allant des bits de poids faibles (représentés à droite de notre dessin) vers les bits de poids fort (à gauche>. Lorsque aucun  nom de champ ne figure devant cette indication de longueur, cela signifie que l’on “saute’ le nombre de bits correspondants (ils ns seront donc pas utilisés)

Avec Ces déclaration, la notation :

mot.donnee 1

désigne un entier signe pouvant prendre des valeurs comprise entre -16 et +15. Elle pourra apparaître à n’importe quel endroit où C autorise l’emploi d’une variable de type int..

Les seuls types susceptibles d’apparaître dans les champs de bits sont int et unsigned int.

Notez que Iorsqu’un champ de type int est de longueur 1, ses valeurs possibles sont 0et et 1comme ce serait le cas avec le type unsigned int.

VIII- LES UNIONS

L’union, en langage C, permet de partager un même emplacement, mémoire par des variables de types différents. Cela peut être utile :

pour économiser les emplacement mémoire en utilisant un même emplacement pendant des phases déférentes  d’un même programme

pour interpréter de plusieurs façons déférentes un même motif binaire. Dans ce sas, il sera alors fréquent que l’union soit elle même associée à des champs de bits.

Examinons d’abord cet exemple :

main()

{
union essai

             {                                                                      donnez un nombre réel :1.23e4
             long n ;                                                           en entier cela fait :1178611712
             float x:

}u ;

printf(’’donnez un nombre réel :’’) ;

scanf(‘‘%f’’, &u.x);

printf(‘’en entier cela fait: %1d’’, u. n) ;

}

la déclaration

union essai

{

long n ;

float x ;

}u ;

réserve un emplacement de 32 bits (ici) qui pourra être considéré tantôt comme un entier long qu’on désignera alors un, tantôt comme un flottant qu’on désignera u.x

D’une manière générale, la syntaxe de a description d’une union est analogue à colle d’une structure. Elle possède un nom de modèle (ici essai)celui-ci peut être ensuite utilise pour définir d’autres variables de ce type.

Par ailleurs, il est  possible de réaliser une union portant sur plus de deux “objets”, chaque objet pouvant être, non seulement d’un type de base, mais également de type structure. En voici un exemple dans lequel nous réalisons une union entre une structure etat telle que nous lavions définie dans le paragraphe précédent et un entier

Struct etat

{

                        unsigned          pret      : 1;
                        unsigned          ok1       : 1;
                        int donnee 1                 :5;
                        int                               
:3;
                        unsigned          ok2       :1;
                        int donnee2                  : 4;

                       };

       union

                      {

int valeur ;

struct etat bits ;

}mot ;

Avec ces déclarations il est alors possible, par exemple, d’accéder à la valeur de mot, considérée comme un entier en la désignant par

Chapitre précedentIndex des Cours

Révisé le :23-Sep-2007| ©2007 www.technologuepro.com