Les types structures et les pointeurs - Université Paris 8
Les types structures et les pointeurs - Université Paris 8
Les types structures et les pointeurs - Université Paris 8
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
L1 Informatique – Université <strong>Paris</strong> 8 - 2010-2011<br />
Programmation Impérative I<br />
Rim Chaabane rchaabane@ai.univ-paris8.fr - Cours 5 -<br />
<strong>Les</strong> <strong>types</strong> <strong>structures</strong> <strong>et</strong> <strong>les</strong> <strong>pointeurs</strong><br />
1. <strong>Les</strong> <strong>types</strong> <strong>structures</strong><br />
<strong>Les</strong> obj<strong>et</strong>s de type structure est comme un tableau, constitué de la réunion d’un ensemble de<br />
valeurs. Mais à la différence des tableaux, ces valeurs ne sont pas nécessairement de même type.<br />
L’accès à une valeur de la structure ne se fait pas avec l’aide d’indice, mais grâce à son nom.<br />
Le type structure perm<strong>et</strong> de rassembler des informations sous une type que vous aurez créé. Par<br />
exemple vous créez le type Personne qui possède un nom, un prénom <strong>et</strong> un âge. Ou bien un type<br />
Point qui a une abscisse x <strong>et</strong> une ordonnée y.<br />
1.1. Déclaration de structure<br />
Pour définir un type structure personne, composée de trois champs : nom, prenom <strong>et</strong> age, il suffit<br />
d’écrire <strong>les</strong> instructions suivantes :<br />
/* définition du type struct personne */<br />
struct personne { char nom[20], prenom[15];<br />
int age ;<br />
} ;<br />
Attention : le point-virgule est nécessaire après l'accolade fermante de la déclaration de la<br />
structure.<br />
- Personne est le nom du type créé<br />
- Nom, prenom <strong>et</strong> age sont <strong>les</strong> champs de ce type<br />
Nous utilisons ensuite ce type pour déclarer des variab<strong>les</strong> :<br />
/*déclaration de variab<strong>les</strong> mere <strong>et</strong> père de type struct personne */<br />
struct personne mere;<br />
struct personne pere;<br />
/* définition du type struct point */<br />
struct point { float x, y ;<br />
} ;<br />
- point est le nom du type créé<br />
- x <strong>et</strong> y sont <strong>les</strong> champs de ce type<br />
/*déclaration de variab<strong>les</strong> A <strong>et</strong> B de type struct point */<br />
struct point A ;<br />
struct point B ;<br />
1
L1 Informatique – Université <strong>Paris</strong> 8 - 2010-2011<br />
Programmation Impérative I<br />
Rim Chaabane rchaabane@ai.univ-paris8.fr - Cours 5 -<br />
La définition d’un type structure ne peut être précédé du mot clé const. Il faut considérer la<br />
définition d’un type struct comme si vous définissez un nouveau type en C. Tout type en C ne<br />
peut être constant. Par contre une donnée d’un type quelconque peut être déclarée comme<br />
constante. Ainsi, il est autorisé de déclarer des valeurs de type struct comme constantes. Par<br />
exemple :<br />
const struct point A ;<br />
Le type des champs d’une structure :<br />
<strong>Les</strong> champs d’une structure peuvent être :<br />
- de n’importe quel type de base<br />
- des tableaux d’éléments de type quelconque<br />
- des <strong>pointeurs</strong><br />
- des <strong>structures</strong> /!\ une structure peut comporter de champs de son type mais il faut le<br />
déclarer au préalable de c<strong>et</strong>te façon :<br />
typedef struct personne pers ; /* pers est de type struct personne */<br />
struct personne { char nom[20], prenom[15];<br />
int age ;<br />
pers * pere ;<br />
};<br />
Déclaration de variable de type structure :<br />
Il existe trois manières de déclarer des variab<strong>les</strong> de type structure en C :<br />
Première méthode<br />
struct personne{<br />
char nom[20];<br />
char prenom[20];<br />
int no_employe;<br />
};<br />
struct personne p1,p2;<br />
p1=p2 ; /* OK */<br />
Deuxième méthode<br />
struct {<br />
char nom[20];<br />
char prenom[20];<br />
int no_employe;<br />
} p1,p2;<br />
struct{<br />
char nom[20];<br />
char prenom[20];<br />
int no_employe;<br />
} p3;<br />
p3 = p1 ; /* OK */<br />
Troisième méthode<br />
struct personne{<br />
char nom[20];<br />
char prenom[20];<br />
int no_employe;<br />
} p1,p2;<br />
struct personne p3;<br />
p3 = p1 ; /* NOK */<br />
La première méthode, définit un type structure dont le nom est personne. C<strong>et</strong>te structure<br />
contient trois champs : un tableau de caractères pour le champ nom, un tableau de caractères<br />
pour le champ prenom <strong>et</strong> un entier pour le champ no_employe.<br />
Plus loin de c<strong>et</strong>te déclaration, nous déclarons deux variab<strong>les</strong> p1 <strong>et</strong> p2 de type struct<br />
personne.<br />
Avec c<strong>et</strong>te méthode, il est possible d’affecter à p1 la valeur de p2, ces deux variab<strong>les</strong> étant<br />
reconnues comme étant du même type.<br />
2
L1 Informatique – Université <strong>Paris</strong> 8 - 2010-2011<br />
Programmation Impérative I<br />
Rim Chaabane rchaabane@ai.univ-paris8.fr - Cours 5 -<br />
La deuxième méthode définit le même type de structure, sauf que c<strong>et</strong>te fois-ci la structure ne<br />
porte pas de nom <strong>et</strong> est immédiatement suivie des noms de variab<strong>les</strong> p1 <strong>et</strong> p2 de ce type<br />
structure. Si plus loin on écrit la même définition du type de structure (sans nom) suivi d’un nom<br />
de variable p3, alors il nous sera possible d’effectuer des opérations entre p1 ou p2 <strong>et</strong> p3.<br />
La troisième méthode définit un type structure avec le nom personne suivi immédiatement des<br />
noms de variab<strong>les</strong> p1 <strong>et</strong> p2. Dans cas si l’on crée comme à la méthode 1, une troisième variable<br />
p3 de type struct personne, il nous sera impossible d’effectuer une quelconque opération<br />
entre p1 ou p2 <strong>et</strong> p3. Ce dernier n’étant pas reconnu comme étant du même type que p1 <strong>et</strong> p2.<br />
De ces trois méthodes c'est la première qui est recommandée, car elle perm<strong>et</strong> de bien séparer la<br />
définition du type structure de ses utilisations.<br />
1.2. Représentation en mémoire<br />
La norme impose aux obj<strong>et</strong>s de type structure <strong>les</strong> deux contraintes suivantes :<br />
- <strong>les</strong> champs doivent être alloués selon leur ordre d’apparition dans la structure ;<br />
- l’adresse d’une structure correspond à l’adresse de son premier champ.<br />
Ainsi dans <strong>les</strong> définitions suivantes, l’ordre d’apparition des champs est le même :<br />
struct s1 {<br />
int n, p ;<br />
float x, y ;<br />
} ;<br />
struct s1 test ;<br />
struct s2 {<br />
int n ;<br />
int p ;<br />
float x ;<br />
float y;<br />
} ;<br />
@m @m+1 @m+2 @m+3<br />
n p x y<br />
test<br />
1.3. Initialisation<br />
Il existe deux méthodes pour initialiser une variable de type structure :<br />
Première méthode : l’initialisation se fait juste au moment de la déclaration de la variable<br />
de type structure.<br />
Deuxième méthode : l’initialisation se fait après la déclaration de la variable de type<br />
structure.<br />
struct personne{<br />
char nom[20];<br />
char prenom[20];<br />
int no_employe;<br />
};<br />
struct personne p1 = {"Dupond", "Jean", 7845}; /* méthode 1 */<br />
struct personne p2 ;<br />
3
L1 Informatique – Université <strong>Paris</strong> 8 - 2010-2011<br />
Programmation Impérative I<br />
Rim Chaabane rchaabane@ai.univ-paris8.fr - Cours 5 -<br />
p2.nom = " Dupond";<br />
p2.prenom = "Jean" ; /* méthode 2 */<br />
p2.no_employe = 7845 ;<br />
Pour accéder aux champs de la variable p2, il suffit de faire suivre le nom de la variable d’un point<br />
suivi du nom du champ.<br />
Nous déclarons <strong>et</strong> initialisons maintenant un tableau de 3 éléments de type struct<br />
personne :<br />
- Avec la méthode 1 :<br />
struct personne tab[3] = { {"Dupond", "Jean", 7845},<br />
{"Le Notre", "Alfred", 4321},<br />
{"Le Gall", "Marc", 5678}} ;<br />
- Ou bien nous l’initialisons avec la méthode 2 :<br />
struct personne tab[3] ;<br />
tab[0].nom = "Dupond";<br />
tab[0].prenom = "Jean";<br />
tab[0].no_employe = 7845;<br />
tab[1].nom = "Le Notre";<br />
tab[1].prenom = "Alfred";<br />
tab[1].no_employe = 1234;<br />
tab[2].nom = "Le Gall";<br />
tab[2].prenom = "Marc";<br />
tab[2].no_employe = 5678;<br />
Nous pouvons, alors noter qu’il est plus simple d’initialiser un tableau de <strong>structures</strong> avec la<br />
première méthode. La seconde méthode est plutôt préconisée dans le cas ou <strong>les</strong> données du<br />
tableau ne sont pas connues en début de programme. Exemple : le programme lit <strong>les</strong> données du<br />
tableau de <strong>structures</strong> à partir d’un fichier de données.<br />
Pour initialiser une variable de type structure, il n’est pas nécessaire de renseigner tous <strong>les</strong> champs<br />
de la structure. Par exemple :<br />
struct personne tab[3] = { {"Dupond", "Jean", 7845}, /* Correct */<br />
{"Le Notre",}, /* Correct */<br />
/* le troisième élément du<br />
tableau n’apparait pas mais<br />
étant donné que c’est le dernier<br />
son absence ne gêne pas la<br />
compilation */<br />
};<br />
Il est également possible d’affecter une valeur de structure à une autre, exemple :<br />
struct personne p1, p2 = {"Dupond", "Jean", 7845};<br />
p1=p2 ;<br />
1.4. Exemple d’utilisation<br />
4
L1 Informatique – Université <strong>Paris</strong> 8 - 2010-2011<br />
Programmation Impérative I<br />
Rim Chaabane rchaabane@ai.univ-paris8.fr - Cours 5 -<br />
2. Introduction à la notion de pointeur<br />
<strong>Les</strong> <strong>pointeurs</strong> perm<strong>et</strong>tent de manipuler directement <strong>les</strong> adresses mémoire des obj<strong>et</strong>s crées ; tels<br />
que <strong>les</strong> variab<strong>les</strong>, <strong>les</strong> <strong>pointeurs</strong> ou encore <strong>les</strong> <strong>structures</strong>.<br />
Soit <strong>les</strong> déclarations suivantes :<br />
int n, t[5] ;<br />
n est une variable de type int <strong>et</strong> t est un tableau de 5 éléments de type int.<br />
Pour obtenir l’adresse de la variable n ou du tableau t il suffit de faire précéder le nom de<br />
l’obj<strong>et</strong> par un & :<br />
&n ;<br />
&t ;<br />
Un pointeur en C contient l’adresse mémoire d’un obj<strong>et</strong>. Pour déclarer par exemple un pointeur<br />
sur l’adresse mémoire de la variable n il suffit d’écrire :<br />
int *pn ; /* on dit que pn est le pointeur sur un obj<strong>et</strong> de type int */<br />
ou<br />
int * pn ;<br />
Il peut donc aussi bien contenir l’adresse de la variable n (qui est destinée à contenir un int)<br />
que l’adresse d’un élément du tableau t comme par exemple &t[2]:<br />
pn = &n ;<br />
ou<br />
5
L1 Informatique – Université <strong>Paris</strong> 8 - 2010-2011<br />
Programmation Impérative I<br />
Rim Chaabane rchaabane@ai.univ-paris8.fr - Cours 5 -<br />
pn=&t[2] ;<br />
Nous initialisons donc de c<strong>et</strong>te façon une variable de type pointeur avec des adresses mémoire<br />
d’obj<strong>et</strong>s de <strong>types</strong> int.<br />
Pour accéder au contenu d’une adresse mémoire, il faut faire appel au pointeur précédé d’une<br />
étoile comme ci-dessous :<br />
*pn = 20 ; /* on place l’entier 20 dans l’adresse mémoire contenue dans pn */<br />
Instruction C description Valeur Résultat<br />
contenue<br />
dans @m<br />
int n = 3; Réserve une adresse 3<br />
mémoire @m qui<br />
contient l’entier 3.<br />
int * pn ; Déclare un pointeur pn 3<br />
sur un entier.<br />
pn = &n ;<br />
Initialise le pointeur pn 3 pn = @m<br />
à l’adresse de n.<br />
int l = *pn ; La variable l prend 3 l = 3<br />
comme valeur le<br />
contenu de l’adresse<br />
pointée par pn.<br />
*pn = 9 ;<br />
L’adresse pointée par 9 pn = @m<br />
pn contient<br />
n = 9<br />
*(@m)= 9<br />
printf("%d\n",<br />
*pn) ;<br />
maintenant la valeur 9.<br />
Affiche le contenu de<br />
@m<br />
Affectation de pointeur :<br />
9 9<br />
p=NULL ; //on crée un pointeur vide qui ne contient aucune adresse<br />
mémoire<br />
p = q ; //p <strong>et</strong> q pointent sur le même obj<strong>et</strong><br />
*p = *q ; //l’adresse pointée par p contiendra l’obj<strong>et</strong> pointé par q<br />
if (p==q) {…} // si p <strong>et</strong> q pointent sur le même obj<strong>et</strong>…<br />
if (p !=q) {…} //si p <strong>et</strong> q ne pointent pas sur le même obj<strong>et</strong>…<br />
p+1 ; //pointe sur l’obj<strong>et</strong> suivant de type t (idem pour p++).<br />
p-q ;<br />
//r<strong>et</strong>ourne le nombre d’obj<strong>et</strong>s de type pointé entre <strong>les</strong><br />
adresses pointées par p <strong>et</strong> q.<br />
6