2/22
Chapitre I
Les Pointeurs
espace
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
4/22
1. Introduction : Définitions de base
Variable
Dans beaucoup de langages, les informations sont manipuler par le biais de
variables.
Une variable est un emplacement ou zone de l’espace mémoire portant un nom
et dont le contenu peut évoluer.
Elle peut être composée d’une ou plusieurs cases mémoire.
Une variable a une adresse qui correspond à l’adresse de la première case. On
l’obtient avec l’opérateur &.
&a = adresse de a
Une variable a un type.
Le type d’une variable indique le nombre de cases qu’elle occupe.
Une variable de type tableau est constituée d’un ensemble de variables de même
type.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
5/22
2. Les Pointeurs
Présentation générale
Le langage C est un langage de programmation qui offre la possibilité d’accéder
aux données dans la mémoire de l’ordinateur à l’aide de pointeurs,
et permet de manipuler des adresses d’objets informatiques (valeurs, variables,
fonctions,...) par le biais des pointeurs.
Autrement, l’utilisation des pointeurs en langage C permet d’avoir un accès à la
mémoire par les adresses sans passer par les identifcateurs.
Ceci permet de se déplacer dans la mémoire et par suite l’optimisation de son
utilisation.
Un pointeur peut être défini comme étant un emplacement en mémoire
destinées à contenir les adresses d’autres objets informatique.
En langage C, les pointeurs jouent un rôle essentiel
pour échanger avec une fonction un gros volume de données. En effet, le
passage des paramètres en C se fait toujours par la valeur, les pointeurs
sont le seul moyen de changer le contenu de variables déclarées dans
d’autres fonctions.
Pour traiter des tableaux dynamiques et de chaı̂nes de caractères.
La manipulation des structures de données avancées (listes, piles, files,
arbres, graphes).
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
6/22
2. Les Pointeurs
Présentation générale (suite)
Syntaxe : L’utilisation d’un pointeur se fait selon la syntaxe suivante :
type * identificateur ;
L’accès à une variable se fait par l’application de l’opérateur * (dit opérateur de
déréférenciation) à un pointeur.
Pour obtenir l’adresse d’une variable on utilise l’opérateur &. Exemple :
int *p,x=2; //p variable destinée à recevoir une adresse d’entier, x recoit 2
p=&x; //p recoit l’adresse de la variable x
printf("%X ",p); //affiche, en hexadecimal l’adresse de x stockée dans p
printf("%d ",*p); //affiche 2, l’entier pointé par p
printf("%d ",x); //affiche 2, la valeur stockée dans la variable x
*p=13; //place la valeur 13 dans l’objet (varaible x) pointé par p
printf("%X ",p); //affiche, en hexadecimal l’adresse de x stockée dans p
printf("%d ",*p); //affiche 13, l’entier pointé par p
printf("%d ",x); //affiche 13, la valeur stockée dans la variable x
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
7/22
2. Les Pointeurs
Remarque
Rappelons qu’en langage C, nous utilisons des variables pour stocker et
manipuler des informations.
La valeur d’une variable se trouve à un emplacement spécifique dans la mémoire.
Pour accéder à cette valeur, on utilise 2 techniques :
Adressage direct : Accès au contenu d’une variable par le nom de la
variable. Exemple :
int x;
x=5; cases mémoires 5
adresses 1E04 1E06 1E08 1E0A
Adressage indirect: Accès au contenu d’une variable, en passant par un
pointeur qui contient l’adresse de la variable. Exemple :
x p
int x=5, *p;
p=&x;
5 1E06
1E04 1E06 1E08 3F06 3F08 3F0A
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
8/22
2. Les Pointeurs
Exemple et exercice
Commenter et exécuter les lignes du programme suivant :
#include<stdio.h>
main()
{ int *p,*q,x=7;
p=&x;
printf("%X \n",p);
printf("%X \n",q);
printf("%d \n",*p);
printf("%d
("%d \n",*q);
\n",x);
printf("%d \n",x);
*p=11;
q=&x;
printf("%X \n",p);
printf("%X \n",q);
printf("%d \n",*p);
printf("%d \n",*q);
printf("%d \n",x);
q=&p;
printf("%X \n",p);
printf("%X \n",q);
printf("%d \n",*p);
printf("%X \n",*q);
printf("%d \n",x);
}
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
9/22
2. Les Pointeurs
Les opérations élémentaires sur pointeurs (1)
Les adresses sont des nombres sur lesquels on peut réaliser des calculs.
C-à-d, on peut ajouter une valeur numérique à une adresse pour accéder à une
autre case mémoire.
En C, il y a des règles particulières pour les opérations de calcul des adresses.
C’est ce qu’on appelle l’arithmétique des pointeurs.
En effet, on peut effectuer des calculs basées sur des pointeurs. Autrement, on
peut ajouter ou soustraire un entier à un pointeur ou faire la différence de deux
pointeurs.
Les seules opérations valides sont les opérations externes :
addition et soustraction des entiers,
la soustraction de pointeurs.
Elles sont définies comme suit (la soustraction d’un entier est considérée comme
l’addition d’un entier négatif) :
p + i = adresse contenue dans p + iútaille(élément pointé par p)
p1 ≠ p2 = adresse contenue dans p1 - adresse contenue dans p2
Si p est un pointeur de type entier, p+1 est donc le pointeur sur l’entier qui suit
immédiatement celui pointé par p.
C-à-d que l’entier qu’on additionne au pointeur est multiplié par la taille de
l’élément pointé pour obtenir la nouvelle adresse.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
10/22
2. Les Pointeurs
Exemple 1
Soit le programme C suivant :
#include <stdio.h>
main()
{ int x=7;
int *p;
p=&x;
printf("Adresse de p est %X \n",p);
printf("Adresse de p+1 est %X \n",p+1);
printf("Adresse de p+2 est %X \n",p+2);
}
Ce programme affiche
Adresse de p est 12FF7C
Adresse de p+1 est 12FF80
Adresse de p+2 est 12FF84
Commentaire
Les résultats sont affichés en hexadécimal (le code des adresses mémoire).
On remarque que dans la ligne 8, le compilateur a ajouté 4 octets à l’adresse
contenue dans p.
La ligne 9 affiche 12FF84, 4 octets de plus par rapport à 12FF80. Il y a donc
une nouvelle fois un décalage de 4 octets.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
11/22
2. Les Pointeurs
Exemple 2
Soit le programme C suivant :
#include <stdio.h>
main()
{ int x=3, y=5,z=7;
int *p, *q, *u;
p=&x; q=&y; u=&z;
printf("Adresse de p-q est %d
%X \n",p-q);
printf("Adresse de p-u est %d
%X \n",p-u);
printf("Adresse de u-q est %d
%X \n",u-q);
}
Ce programme affiche
Adresse de p-q est 1
Adresse de p-u est 2
Adresse de u-q est -1
FFFFFFFF
Commentaire
Remarque
En
Le fait, on peut
résultat de ladire que la soustraction
soustraction de deux P1
de deux pointeurs pointeurs n’est que l’opération
et P2 est:
inverse de l’incrémentation : si p1 et p2 sont des pointeurs de même type tels que l’on
- Négatif, si P1 précède P2
- Zéro, si P1 = P2
ait p2 = p1 si
- Positif, + i,
P2alors p2 ≠
précède P1p1 = i ou p1 ≠ p2 = ≠i.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
12/22
2. Les Pointeurs
Les opérations élémentaires sur pointeurs (2)
Rappelons qu’en C, les pointeurs servent à manipuler des adresses.
Mais leur intérêt principal est de permettre d’appliquer aux objets pointés des
opérations comparables à celles qu’on applique à une variable ”ordinaire”.
Incrémenration :
– *p+=2 équivalent à *p=*p+2 (augmenter de 2 la valeur de l’objet pointé par p).
– (*p)++ équivalent à *p=*p+1 (augmenter de 1 la valeur de l’objet pointé par
p).
Attention ! aux parenthèses, *p++ serait équivalent à *(p++)
– On trouve la même chose si on utilise l’opérateur --.
Affectation :
– Si p et q sont deux pointeurs alors l’instruction p=q signifie que p et q
pointent sur le même objet.
– L’affectation p=q n’est correcte que si p et q sont de même type.
Le pointeur NULL :
– Le pointeur NULL est une adresse fictive ne correspondant à aucune zone de
mémoire accessible.
– Dans la plupart des systèmes, il vaut 0. C’est une constante définie
notamment dans le fichier stdio.h.
– La constante NULL sert à la fois pour initialiser un pointeur et pour vérifier
qu’un pointeur est initialisé.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
13/22
2. Les Pointeurs
Exemple et exercice
Soit le programme C suivant :
#include <stdio.h>
main()
{ int x=1, y=2,z=3;
int *p, *q;
p=&x; q=&z;
*p=(*q)++;
p=q;
q=&y;
*p-=*q;
++*q;
*p*=*q;
x=++*q**p;
p=&x;
*q=*p/=*q;
}
1 Commenter ligne par ligne de ce programme.
2 A chaque ligne du programme, donner les valeurs stocker dans les variables x, y,
z et les valeurs des variables pointées par les pointeurs p et q.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
14/22
3. Pointeurs et tableaux
Il existe une relation très étroite entre les pointeurs et les tableaux. Rappelons que :
Un tableau est un ensemble de variables de même type qui sont stockés les unes à la suite des
autres en mémoire.
Un pointeur contient une adresse qui lui permet d’accéder à une variable en mémoire.
Il est possible, grâce à l’arithmétique des pointeurs, de modifier le contenu du pointeur afin
d’accéder à une autre variable.
Définir des pointeurs vers les tableaux est l’unique solution pour passer un tableau à une fonction.
En C, le nom d’un tableau correspond à l’adresse du premier élément du tableau. En d’autre terme, si
int tab[20]; est une déclaration d’un tableau de 20 entiers portant le nom tab alors
&tab[0] et tab sont une seule et même adresse. C-à-d, le nom d’un tableau est un pointeur (constant qui
ne peut pas être modifié) sur le premier élément du tableau.
Exemple :
1.#include<stdio.h>
2. main()
3. {
4. int tab[5]={1,2,3,4,5};
5. int *pTab;
6. pTab=tab;
7. printf(" %d \n",*tab); // Affiche : 1
8. printf("%d \n",pTab[0]); // Affiche : 1
9. }
Commentaire : Les lignes 7 et 8 montrent que le nom d’un tableau peut être utilisé comme un pointeur sur
son premier élément.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
15/22
3. Pointeurs et tableaux
Ainsi,
nous pouvons employer la notation pointeur pour désigner les éléments du tableau.
Inversement, nous pouvons utiliser la notation tableau avec un pointeur.
Exemple :
1.#include<stdio.h>
2. main()
3. {
4. int tab[10];
5. int *pTab;
6. pTab=tab;
7. *tab=1;
8. pTab[0]=1;
9. *(tab+1)=2;
10. pTab[1]=2;
11. }
Commentaire :
– A la ligne 6, le pointeur pTab est initialisé avec l’adresse de tab.
– Les lignes 7 et 8 initialisent le premier élément du tableau tab avec 1. A la ligne 7, le tableau est
manipulé avec la notation pointeur *, alors qu’à la ligne 8, c’est le pointeur qui est utilisé avec la
notation tableau [].
– Même chose, pour les lignes 9 et 10.
D’une façon générale, on peut toujours utiliser la notation tableau avec un pointeur ou la notation pointeur
avec tableau. En C, on utilise souvent la notation tableau dans tous les cas.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
16/22
3. Pointeurs et tableaux
Remarque :
Si pTab pointe sur un élément quelconque d’un tableau, alors pTab+1 pointe sur
l’élément suivant. Plus généralement,
– pTab+i pointe sur le i-ième élément devant pTab.
Ainsi, si pTab pointe sur tab[0] alors
– *(pTab+1) désigne le contenu de tab[1].
– *(pTab+i) désigne le contenu de tab[i].
Notons que, le compilateur se charge d’effectuer les calculs lorsqu’on incrémente
et décrémente des pointeurs. En effet,
– Pour l’instruction tab+3 (ou *(pTab+3) le pointeur ne se déplace pas de 3
octets à droite mais de 3 objets après l’adresse de tab.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
17/22
3. Pointeurs et tableaux
Exemple et exercice :
Commenter et donner le résultat du programme C suivant :
1.#include<stdio.h>
2. main()
3. {
4. int tab1[20], tab2[10];
5. int i,j;
6. for(j=0,i=0;i<20;i++)
7. {
8. *(tab1+i)=i+1;
9. if(*(tab1+i)%2==0)
10. {
11. *(tab2+j)=*(tab1+i);
12. j++;
13. }
14. }
15. for(i=0;i<20;i++)
16. printf("%d ",tab1[i]);
17. printf("\n");
18. for(i=0;i<10;i++)
19. printf("%d ",tab2[i]);
20. printf("\n");
21. }
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
18/22
4. Pointeurs et fonction
Pointeur en paramètre de fonction
L’une des utilités des pointeurs est de permettre à des fonctions d’accéder aux
données elles même et non à des copies.
En effet, la fonction recoit l’adresse de l’argument formel, ensuite elle travaille
directement sur cette adresse. En conséquence, elle peut modifier la valeur de
l’argument formel.
Exemple 1 :
calcul de min de deux entiers calcul de puissance d’un entier
Définition de la fonction : Définition de la fonction :
void min(int a, int b, int *m) void puissance(int x, int n, int *res)
{ {
if(a<b) int i;
*m=a; *res=a;
else for(i=1;i<n;i++)
*m=b; *res=(*res)*a;
} }
Appel de la fonction : Appel de la fonction :
min(12,19,&m); puissance(3, 4, &res);
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
19/22
4. Pointeur et fonction
Pointeur en paramètre de fonction (suite)
Exemple 2 : Passer un tableau à une fonction
En fait, pour passer un tableau à une fonction, on doit toujours envoyer
l’adresse de ce tableau.
Toute fonction qui récupère un tableau doit définir un argument de type
pointeur pour stocker l’adresse du tableau.
L’exemple suivant montre la définition et l’utilisation d’une fonction qui permet
la saisie, au clavier, des élément d’un tableau :
Définition de la fonction :
void saisieTableau(int *tab, int taille)
{ int i;
for(i=0;i<taille;i++)
{
printf("tab[%d]= ",i+1);
scanf("%d",&tab[i]);
}
}
Appel de la fonction :
saisieTableau(tab, 20);
Exercice : Écrire un programme en C, en utilisant des fonctions, permettant
la saisie, l’affichage et l’échange des valeurs d’un tableau.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
20/22
4. Allocation dynamique de mémoire
L’allocation de mémoire est nécessaire pour les raisons suivantes :
lorsque la taille mémoire nécessaire au stockage des données n’est pas
connue à l’écriture du programme, le programmeur doit demander au
système d’exploitation de lui allouer de la mémoire durant l’exécution.
Cette mémoire est allouée dynamiquement dans une zone dédiée appelée
tas (heap).
L’accès à la mémoire doit être réglementé pour qu’il n’y ait pas de conflits
tels que des zones utilisées deux fois.
La fonction malloc() de la bibliothèque <stdlib> permet d’allouer un bloc de
mémoire (un ensemble de cases contigues).
Elle prend en paramètre le nombre de cases à allouer et elle retourne un
pointeur vers la première case de la zone allouée. En effet :
malloc(n)≠æ retourne l’adresse d’un bloc en mémoire de n octets libres ou la
valeur zéro s’il n’y a pas assez de mémoire.
La réservation de la mémoire pour des données d’un type dont la grandeur varie
d’une machine à l’autre exige la grandeur effective d’une donnée de ce type.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée
21/22
4. Allocation dynamique de mémoire
L’opérateur unaire sizeof() aide à préserver la portabilité du programme. En
fait,
L’opérateur sizeof() prend un type en paramètre et retourne sa taille en
nombre de cases (i-e. en nombre d’octets).
L’opérateur sizeof() n’est pas une fonction.
– sizeof(variable) ≠æ retourne la grandeur de la variable variable.
– sizeof(const) ≠æ retourne la grandeur de la constante const.
– sizeof(type) ≠æ retourne la grandeur pour un objet du type type.
La fonction free() complète malloc().
Elle permet de libérer un bloc de mémoire.
Elle prend en paramètre l’adresse de la zone de mémoire à libérer. Cette adresse
doit avoir été retournée par malloc() et elle ne doit pas avoir déjà été libérée.
Une zone de mémoire libérée ne doit plus être utilisée.
Filière SMIA Parcours SMI & SMA Semestre3 Programmation C avancée