Les types composés : struct, union &
enum
Les types composés : structures
Syntaxe
Déclaration de nouveau type de structure :
struct nomStruct{
type1 champ1;
type1 champ2;
...
typeN champN;
};
Déclaration de variable structurée de type structure nomStruct (préalablement défini) :
struct nomStruct variable;
Définition
• Une structure (ou enregistrement) est une suite finie d’éléments de types différents,
• Chaque élément de la structure, appelé : membre ou champ, est désigné par un
identificateur,
• Elle permet de grouper plusieurs valeurs dans une seule variable.
12/12/2024 80
Les types composés : structures
Exemple
compte = numBanque + numCompte + solde
• La structure compte est composée de champs :
• nommés : banque, compte, solde
• et typés : int pour numBanque et numCompte, float pour solde
• Les différents éléments d’une structure n’occupent pas nécessairement des
zones contigues en mémoire (Contrairement aux tableaux).
Déclaration de type structure compte :
struct compte{
int numBanque;
int numCompte;
float slode; };
Déclaration de deux variable structurées de type compte :
struct compte cpt1, cpt2;
12/12/2024 81
Les types composés : structures
Double déclaration d’une structure
struct compte{
int numBanque;
int numCompte;
float solde;
} cpt1, cpt2;
/* cpt1 occupera (sizeof) 12 octets en mémoire. */
12/12/2024 82
Les types composés : Manipulation des structures
Syntaxe
[Link]
Manipulation
• Une variable structurée peut être manipulée :
• champ par champ (opérations de lecture et mise à jour, etc.),
• ou comme un tout (opérations de copie, initialisation, etc.)
• L’opérateur point permet de référencer le champ champ de la variable variable
Syntaxe
Dans une expression :
float s = [Link] + [Link] ;
Modification :
[Link] += 101.1;
12/12/2024 83
Les types composés : Manipulation des structures
Copie de structure
L’opérateur = peut être utilisé sur des variables structurées, Il permet de copier champ à
champ (ce n’était pas possible avec les tableaux !)
Syntaxe
newCpt = cpt1;
Est équivalent à :
[Link] = [Link];
[Link] = [Link];
[Link] = [Link] ;
12/12/2024 84
Les types composés : Manipulation des structures
Syntaxe d’initialisation à la déclaration de variable
struct nomStruct variable ={exp1, ..., expN} ;
Syntaxe d’initialisation par copie
struct nomStruct newVariable = oldVariable ;
Manipulation
• L’initialisation à la déclaration est possible (comme pour tableau),
• L’ordre des expressions (exp1, exp2, etc.) est le même que celui des champs,
• Les champs manquants sont initialisés à 0 ou NULL,
• On peut imbriquer les initialiseurs entre { et } (structures et/ou tableaux imbriqués)
• Exemple : struct compte cpt ={180, 1234, 1.1};
12/12/2024 85
Les types composés : Manipulation des structures
Question
Afficher une variable structurée de type compte.
Réponse
Il faut afficher les champs un par un, à la main ! Exemple :
printf("Compte\n");
printf("----\n");
printf("Banque : %i\n", [Link]);
printf("Numéro : %i\n", [Link]);
printf("Solde : %f\n", [Link]);
12/12/2024 86
Les types composés : Manipulation des structures
Structure et typedef
struct compte{
int numBanque;
int numCompte;
float slode;
};
typedef struct compte compteType;
Typedef int unnouveauint;
int main(){
compteType c;
unnouveauint i;
...
}
12/12/2024 87
Les types composés : Manipulation des structures
Réponse
#include <math.h>
Question
#include <stdio.h>
struct complexe{
Calculer et afficher la norme du nombre complexe 2 +3i en représentant le nombre
double reelle;
complexe par une structure C.
double imaginaire; };
typedef struct complexe complexe;
int main(){
complexe z = {2,3};
double norme = sqrt([Link] * [Link] + [Link]
*[Link]);
printf("norme de %.2f+%.2fi : %f", [Link],[Link],
norme);
/* resultat : 3.605551 * /
return 0;
}
12/12/2024 88
Les types composés : Structures et Tableaux
Tableaux dans structure
struct id{
char nom[50];
char prenom[50];
int dateNaissance[3];
};
Remarques
• Des champs tableaux peuvent être dans une structure,
• Exemple d’initialisation : (imbrication des initialiseurs)
struct id
id1={"SADIQ","Abderrahmane",{1,1,2019}};
• Accéder à un élément de la même façon : (mois de naissance)
[Link][1];
• Une variable de type struct id occupe 112 octets.
12/12/2024 89
Les types composés : Structures et Tableaux
Tableaux dans structure
id2 = id1;
/* est equivalent à */
[Link][0] = [Link][0];
...
[Link][49] = [Link][49];
[Link][0] = [Link][0];
...
[Link][49] = [Link][49];
[Link][0] = [Link][0];
[Link][1] = [Link][1];
[Link][2] = [Link][2];
Remarques
• L’affectation (=) et l’initialisation copient récursivement les champs.
12/12/2024 90
Les types composés : Structures et Tableaux
Structures et tableaux
struct id{
char nom[TAILLE];
char prenom[TAILLE];
int dateNaissance[3]; /
};
struct id groupe[MAX];
Questions
• Comment accéder au j-ème caractère du prénom du i-ème membre du groupe ?
• Comment modifier le jour de naissance du i-ème membre du groupe ?
Réponse
• accès au j-ème caractère du prénom du i-ème membre du groupe :
groupe[i].prenom[j];
• modifier le jour de naissance du i-ème membre du groupe :
12/12/2024 groupe[i].dateNAissance[0] = jour; 91
Les types composés : Structures
Présentation en mémoire ?
Exemple:
struct ma_struct
{ char a;
short b;
};
struct ma_struct tab[7]
12/12/2024 92
Structure en Paramètres
Utilisation
• Une variable de type structure peut bien entendu être utilisée comme
paramètre d’une fonction.
• Le passage se déroule alors comme pour une variable de type simple : on doit
choisir entre un passage par valeur ou par adresse.
• Supposons qu’on a une fonction recevant en paramètre l’adresse d’une
variable de type : struct personne
void ma_fonction(struct personne* x);
• Alors la modification d’un de ses champs passe par l’utilisation de l’opérateur
*.
• Par exemple, si on veut changer l’âge de la personne x, on utilisera
l’expression : (*x).age = 99;
12/12/2024 93
Structure en Paramètres
Utilisation
• Notez bien l’utilisation des parenthèses. En effet, on veut appliquer
l’opérateur * seulement à l’expression x, qui est de type struct personne*.
• On obtient alors l’expression *x, qui est de type struct personne.
• On peut alors appliquer l’opérateur . à cette expression *x, afin d’obtenir son
champ age.
• Il serait impossible d’appliquer directement l’opérateur . à x, car cet opérateur
ne s’applique qu’à une structure. Or, x n’est pas une structure, mais l’adresse
d’une structure.
• Cependant, un opérateur spécial peut être utilisé pour accéder à un champ à
partir de l’adresse d’une structure. Il s’agit de l’opérateur ->, qu’on utilise de
la façon suivante :
x->age = 99;
12/12/2024 94
Exercice
La FPT organise un tournoi de tennis de table en double. Chaque équipe
désirant participer se compose 2 joueurs et possède un nom. Chaque joueur
est caractérisé par un nom, un prénom, un âge et un score allant de 0 à 100
indiquant son niveau.
1. Définissez les types structures joueur et equipe correspondants.
2. Écrivez une fonction saisis_joueur permettant de saisir les caractéristiques
d’un joueur. Le paramètre doit être passé par adresse.
3. En utilisant la fonction précédente, écrivez une fonction saisis_equipe
permettant de saisir les caractéristiques d’une équipe. Le paramètre doit
être passé par adresse.
4. Écrivez une fonction main qui crée et remplit un tableau de N équipes en
utilisant la fonction saisis_équipe.
5. Écrivez des fonctions affiche_joueur et affiche_equipe pour afficher à
l’écran les équipes et leurs joueurs.
6. Complétez la fonction main précédente de manière à afficher les équipes
après la saisie.
12/12/2024 95
Les types composés : Unions
Syntaxe
union nomUnion {
type1 champ1;
...
typeN champN;
};
Définition
• Une union est composée de champs : nommés et typés,
• Elle désigne un ensemble de variables de types différents susceptibles d’occuper
alternativement une même zone mémoire,
• Elle permet de définir une variable pouvant être d’un type au choix parmi un ensemble
fini de types.
12/12/2024 96
Les types composés : Unions
Syntaxe
union nomUnion {
type1 champ1;
...
typeN champN;
};
A noter…
• On ne peut utiliser qu’un champ à la fois : les champs partagent le même
emplacement en mémoire,
• La place réservée (octets) en mémoire pour la représenter correspond à
la taille du champ le plus grand.
12/12/2024 97
Les types composés : Unions
Exemple 1
union jour {
char lettre;
int numero;
};
Exemple 2
union nombre {
int entier;
double flottant;
};
A noter…
• Occupation en mémoire de jour : 4 octets,
• Occupation en mémoire de nombre : 8 octets.
12/12/2024 98
Les types composés : Unions
Présentation en mémoire ?
Exemple:
union taille
{ short centimetres;
float metres;
};
union taille t;
12/12/2024 99
Les types composés : Manipulation des Unions
Exemple
union nombre nbr;
union jour hier, demain;
[Link] = 100;
[Link] *= 3;
[Link] = ’V’;
printf("hier = %c\n",[Link]);
[Link] = 4;
[Link] = ([Link] + 2)%7;
printf("demain = %d\n",[Link]);
Déclaration et utilisation
• Les unions se déclarent et s’utilisent comme les structures,
• la variable hier de type union jour peut être soit un entier soit un caractère.
12/12/2024 100
Les types composés : Unions
Exemple (bout de code)
union nombre nbr;
double x;
[Link] = 100; //ok
[Link] = 102; //ok
x=[Link]; // non
A noter…
Il ne faut pas écrire dans un champ puis lire depuis un autre : il faut se
souvenir du champ actif !
12/12/2024 101
Les types composés : Unions
Solution
typedef union {
int ent;
double flot;
} valeur;
typedef struct {
int type;
valeur val;
} nombre;
nombre nbr={0, 100};
Utiliser discriminant pour se souvenir du champ actif
• le champ entier type dans struct nombre sert de discriminant :
• si [Link]==0, alors on utilise [Link]
• si [Link]==1, alors on utilise [Link]
12/12/2024 102
Les types composés : Énumérations
Syntaxe
enum modele{constante0, constante1, ..., constanteN};
Définition
• Une énumération permet de définir un type par la liste des valeurs qu’il peut
prendre,
• Elle est définie par le mot-clef enum et un identificateur de modèle, suivis de
la liste des valeurs possible que peut prendre une variable de ce type,
• En réalité, les constantes sont représentées comme des int : les valeurs
possibles constante0, constante1,...,constanteN sont codées par des entiers
de 0 à N.
12/12/2024 103
Les types composés : Énumérations
Exemple
enum booleen {faux, vrai};
enum couleur{jaune, rouge, bleu, vert};
enum booleen b;
enum couleur c;
b = vrai; c=bleu;
printf("b = %d, c=%d\n",b,c);
Définition
• Le type enum booléen associe l’entier 0 à la valeur faux et l’entier 1 à la valeur
vrai,
• On peut modifier le codage par défaut des valeurs de la liste lors de la
déclaration du type énuméré. Exemple :
enum couleur {jaune=12, rouge=23, bleu=33 , vert=40};
12/12/2024 104
Exercice
Une entreprise a besoin de stocker dans un tableau le nombre
d’heures travaillées par jours pendant une semaine.
• Définissez un type énuméré semaine pour représenter les 7
jours de la semaine.
• Écrivez une fonction afficher_semaine permettant d’afficher
le mot (nom du jour) correspondant au numéro du jours
passé en paramètre.
• Écrivez une fonction saisir_nbHeures demandant à un
utilisateur de remplir le tableau d’heures mensuelles.
12/12/2024 105
Les types composés : Nommage de types
Syntaxe
typedef type nom_du_type;
Explication
• Il est possible de définir un nouvel identificateur pour un type, en utilisant
l’instruction typedef.
• Si le type est anonyme, alors il le fait de le nommer le rend plus facile à
manipuler. S’il a déjà un nom, alors ce nouveau nom ne supprime pas l’ancien.
• La principale utilisation de typdef concerne les types structurés, unions et
énumérés, car elle évite de devoir répéter les mots clés struct, union ou enum
en permanence.
12/12/2024 108
Les types composés : Nommage de types
Exemple
struct s_joueur
{ char prenom[10];
char nom[10];
short age;
short niveau;
};
typedef struct s_joueur joueur;
Explication
• Ici, on déclare d’abord un type structure, dont le nom complet est struct s_joueur.
• On définit ensuite un nouveau « joueur » pour ce type en utilisant typedef.
• Par la suite, on peut donc utiliser n’importe lequel de ces deux noms, qui font
référence au même type : soit struct s_joueur, soit joueur.
• Pour déclarer une nouvelle variable x de ce type, on peut donc écrire : joueur x;
• Mais on peut aussi continuer à faire : struct s_joueur x;
12/12/2024 109
Les types composés : Nommage de types
A noter…
Il est possible de définir le type que l’on veut nommer directement dans
l’instruction typedef, de la façon suivante :
typedef struct s_joueur
{ char prenom[10];
char nom[10];
short age;
short niveau;
} joueur;
Ou encore
typedef struct
{ char prenom[10]; char nom[10];
short age;
short niveau;
} joueur;
12/12/2024 110
LES POINTEURS
Les pointeurs
Rappel
• Toute variable manipulée dans un programme est stockée quelque part en
mémoire centrale,
• La mémoire est constituée d’octets qui sont identifiés de manière univoque
par un numéro qu’on appelle adresse,
• Donc : pour retrouver/manipuler une variable, il suffit donc de connaître
l’adresse de l’octet où elle est stockée,
• Ou aussi : pour retrouver/manipuler une variable qui recouvre plusieurs
octets contigus, il faut l’adresse du premier de ces octets.
12/12/2024 112
Les Pointeurs
Imaginez…
Si on manipule directement les adresses des variables dans un programme
12/12/2024 113
Les Pointeurs
La solution est déjà là…
• On désigne les variables par des identificateurs et non pas par leur adresse
mémoire,
• Le compilateur fait le lien entre l’identificateur d’une variable et son adresse
en mémoire.
À noter que…
il est parfois très pratique de manipuler directement une variable par
son adresse.
Définition: Lvalue (left value)
• Lvalue = tout objet pouvant être placé à gauche d’un opérateur d’affectation,
• Une Lvalue est caractéerisée par :
• son adresse mémoire à partir de laquelle l’objet est stocké,
• sa valeur qui est stocké à cette adresse.
12/12/2024 114
Les Pointeurs
Exemple
int a,b;
a=6;
b=a;
Imaginez…
Explication
Deux variables différentes ont des adresses différentes,
L’affectation b = a; n’opère que sur les valeurs des variables,
Chaque variable est stockée sur 4 octets (int) : la valeur de a est stockée sur les
octets d’adresse 2293572 à 2293575
12/12/2024 115
Les Pointeurs
Adresse d’une variable
• L’opérateur & permet d’accéder à l’adresse d’une variable
• &i n’est pas une Lvalue, c’est une constante,
• On ne peut pas avoir &i à gauche d’un opérateur d’affectation.
Question
Comment peut-on manipuler les adresses alors ?
Réponse
• Il faut recourir à un nouveau type de variable : les pointeurs.
12/12/2024 116
Les Pointeurs
Syntaxe
Type *nomDuPointeur;
Adresse d’une variable
• Un pointeur est un objet Lvalue dont la valeur est égale à l’adresse d’un
autre objet de type type,
• type est le type de l’objet pointé,
• C’est une déclaration de nomDuPointeur associé à un objet,
• L’identificateur nomDuPointeur est donc un identificateur d’adresse : comme
étant un Lvalue, sa valeur est modifiable.
Exemple
int n=9; Int *p;
p=&n;
12/12/2024 117
Les Pointeurs
Exemple
Int *p; int n=9;
p=&n;
En mémoire
A noter…
La valeur d’un pointeur est toujours un entier : éventuellement un entier long,
Le type d’un pointeur dépend du type de l’objet vers lequel il pointe : *int, *char,
*float ou d’autres . . .
12/12/2024 118
Les Pointeurs
Question
• Si la valeur d’un pointeur est toujours un entier, donc pourquoi avoir des pointeurs typés ?
• C.à.d : pourquoi il faut à chaque fois donner le type de l’objet vers lequel il pointe ?
int *i; char *c; Float *f;
Réponse
• Le type d’un pointeur dépend du type de l’objet vers lequel il pointe puisque :
• à l’interprétation de la valeur d’un pointeur, il faut s’assurer du nombre des octets
pointés,
• lors de la manipulation de la valeur pointée, il faut préciser le type de la zone
pointée, et puis les valeurs stockées.
Exemple
• Un pointeur sur un objet de type char : la valeur du pointeur donne l’adresse de l’octet
ou l’objet est effectivement stocké,
• un pointeur sur un objet de type int : la valeur du pointeur donne seulement l’adresse
du premier octet des 4 octets où l’objet est stocké.
12/12/2024 119
Les Pointeurs
L’opérateur unaire d’indirection *
• Il permet d’accéder directement à la valeur de l’objet pointé
• Exemple : si p est un pointeur vers un entier a, *p désigne la valeur
de a : a est *p sont identiques (même adresse et valeur).
Exemple
int a=6; int *p;
p=&a;
printf("Valeur de la variable pointée par p est : %d\n", *p);
printf("Valeur de la variable a est : %d\n",a);
Résultats
Valeur de la variable pointée par p est : 6
Valeur de la variable a est : 6
12/12/2024 120
Les Pointeurs
Les pointeurs en C :
• & : obtenir l’adresse d’une variable existante,
• * : accéder au contenu stocké à une adresse valide,
• = : passer des adresses en argument, les retourner, les copier,
• effectuer des opérations limitées sur les adresses.
Exemple
int i, t[5]; float f;
int *p;
&i /* adresse de i */
&(t[1]) et &t[1] /*adresse de t[1] */
&(t+1) et &(i+1) */ invalides */
p = &i; /* p pointe sur i */
p = &f; /* non défini */
p =&(i+1); /* erreur de syntaxe */
12/12/2024 121
Les Pointeurs
Exemple (bout de code): Manipulation de p et *p
int main(){
int a = 10, b = 60;
int *p1, *p2;
p1 = &a;
p2 = &b;
*p1 = *p2;
}
En mémoire: avant et après l’affectation
12/12/2024 122
Les Pointeurs
En détails…
main()
{ int i = 3;
int *p;
p = &i;
printf("*p = %d \n",*p);
}
imprime *p = 3.
Dans ce programme, les objets i et *p sont identiques : ils ont mêmes adresse et valeur.
Nous sommes dans la configuration :
Cela signifie en particulier que toute modification de *p modifie i.
12/12/2024 123
Les Pointeurs
En détails…
main() main()
{ {
int i = 3, j = 6; int i = 3, j = 6;
int *p1, *p2; int *p1, *p2;
p1 = &i; p1 = &i;
p2 = &j; p2 = &j;
*p1 = *p2; p1 = p2;
} }
Avant la dernière affectation de chacun de ces programmes, on est dans une
configuration du type :
12/12/2024 124
Les Pointeurs
En détails…
Après l’affectation *p1 = *p2; du premier
programme, on a:
Par contre, l’affectation p1 = p2 du second
programme, conduit à la situation:
12/12/2024 125