20/12/2020
Langage de programmation C
Chap. 05
Les pointeurs
a. Sabraoui Année universitAire 2020/2021
Sommaire
1. Mémoire de l’ordinateur
2. Création, Définition, Déclaration et Initialisation d’un
pointeur
3. Opérations élémentaires sur les pointeurs
4. Pointeurs et tableaux 1D et 2D
5. Arithmétiques des pointeurs
6. Allocation dynamique: Fonctions malloc et free
1
20/12/2020
La mémoire de votre ordinateur
La compréhension des pointeurs, repose sur le fonctionnement de
la mémoire de votre ordinateur.
Tout d’abord un bref rappel sur le fonctionnement des ordinateurs.
Un ordinateur comporte un processeur et de la mémoire. Les deux
sont connectés par un bus, c'est-à-dire un ensemble de lignes
électriques
Mémoire(RAM) Adresses
var
1000
Processeur 100 1004
Bus 1008
1012
1016
1020
Un octet = 8 bits 1024
La mémoire de votre ordinateur
La mémoire vive de votre ordinateur est constituée de milliers
d’emplacements mémoire rangés de façon séquentielle. Chaque
emplacement a une adresse unique, comprise entre 0 et une valeur
maximale qui dépend de la quantité de mémoire installée sur votre
ordinateur.
Lorsqu’une variable est déclarée, le compilateur réserve un
emplacement mémoire (une série de bits qu’on ne lit/écrit pas un
par un mais par paquets de huit, ce que l'on appelle un octet
(byte).) avec une adresse unique pour stocker cette variable
(qu’on peut relire plus tard) .
Le compilateur associe l’adresse au nom de la variable. Quand le
programme utilise le nom de la variable, il accède
automatiquement à l’emplacement mémoire correspondant
2
20/12/2020
Création d’un pointeur
Dans la figure précédente nous avons déclaré une variable var
et nous l’avons initialisée à 100. Le compilateur a réservé un
emplacement mémoire à l’adresse 1004, qu’il associe au nom
de la variable.
L’adresse de cette variable var est un nombre, ce qui nous
permet de l’utiliser comme n’importe quel autre nombre en
langage C.
Si vous connaissez l’adresse d’une variable, vous pouvez créer
une autre variable pour y stocker l’adresse de la première.
La première étape consiste à déclarer la variable dans laquelle
on stockera l’adresse de var.
Création d’un pointeur
Soit p_var la variable dans laquelle on stockera l’adresse de var,
d’après le schéma ci-dessous un emplacement mémoire a été
réservé pour p_var
1001 1002 1003 1004 1005
? 100
p_var var
L’étape suivante consiste à stocker l’adresse de var dans la
variable p_var, celle-ci représente maintenant l’emplacement
mémoire de la variable var
1001 1002 1003 1004 1005
1004 100
p_var var
3
20/12/2020
Définition et déclaration
Définition
Un pointeur est une variable qui contient l'adresse d'une autre
variable.
Déclaration
On déclare un pointeur par l’instruction :
type * nom_du_pointeur ;
• Où: type est le type de la variable pointée,
• L’identificateur nom_du_pointeur est le nom de la variable pointeur
et * est l’opérateur qui indiquera au compilateur que c’est un pointeur.
Exemple: int *p; float *p_float; char *p_char;
• On dira que p est un pointeur sur une variable de type int , ou bien p peut
contenir l'adresse d'une variable de type int
• *p est de type int, c’est l’emplacement mémoire pointé par p.
Initialisation
Déclarer un pointeur n’est pas suffisant, si vous ne le
faites pas pointer sur une variable, il est inutile.
Un pointeur doit contenir l’adresse d’une variable en
utilisant l’opérateur &, quand il est placé avant le
nom de la variable, l’opérateur d’adresse renvoie
l’adresse de cette variable.
Son initialisation est une instruction de la forme:
pointeur = &variable;
4
20/12/2020
Initialisation
Exemple:
int A, B, *P; /*supposons que ces variables occupent
la mémoire à partir de l’adresse 1000 */
A = 10;
B = 50;
P = &A ; // se lit : mettre dans P l’adresse de A
B = *P ; /* se lit : mettre dans B le contenu de
la variable pointée par P */
*P = 20; /*mettre la valeur 20 dans la variable
pointé par P*/
P = &B; // P pointe sur B
Exemples
Exemple 1:
void main()
{
int v=12;
int u=10;
int *vP; /* pointeur sur int */
vP = &v; /* affectation du pointeur */
u = *vP;
printf("u=%d v=%d\n",u,v);
*vP = 25;
printf("u=%d v=%d\n",u,v);
printf("*vP=%d",*vP);
}
5
20/12/2020
Exemples
Exemple 2:
#include <stdio.h>
#include <conio.h>
Void main( )
{
float a , *p; /*supposons que ces variables sont représentées en
mémoire à partir de l’adresse 1000*/
p = &a;
printf("Entrer une valeur : ");
scanf("%f" ,p); // on saisie la valeur 12.4
printf("\nAdresse de a = %d Contenu de a = %f",p,*p);
*p += 0.4;
printf("a = %f *p = %f ", a,*p);
}
Opérations élémentaires sur les pointeurs
L’opérateur & : ’adresse de’ : permet d’obtenir l’adresse d’une
variable.
L’opérateur * : ’contenu de’ : permet d’accéder au contenu
d’une adresse.
Si un pointeur P pointe sur une variable X, alors *P peut être
utilisé partout où on peut écrire X.
Exemple:
• int X=1, Y, *P Après l’instruction, P = &X ; On a :
• Y=X+1 équivalente à Y = *P + 1
• X += 2 équivalente à *P += 2
• ++X équivalente à ++ *P
• X++ équivalente à (*P)++
6
20/12/2020
Opérations élémentaires sur les pointeurs
Le seul entier qui puisse être affecté à un pointeur d’un type
quelconque P est la constante entière 0 désignée par le symbole
NULL défini dans <stddef.h>.
On dit alors que le pointeur P ne pointe ’nulle part’.
Exemple:
#include <stddef.h>
int *t, x , *r;
short y = 10 , *pt = &y;
t = NULL ; /* Correct */
t=0; /* Correct */
x=0;
t=x; /* Incorrect ! bien que x vaille 0 */
r = &x ;
t=r; /* Correct : p et r deux pointeurs de même type*/
t = pt ; /* Incorrect : p et pt deux pointeurs de type différent */
Exercices
Exemple:
Trouvez les erreurs dans les suites d’instruction suivantes :
1 int *p , x = 34; *p = x; *p = x est incorrect parce que le pointeur p n’est
pas initialisé
2 int x = 17 , *p = x; *p = 17; p = x est incorrect. Pour que p pointe sur x :
p = &x
3 double *q; int x = 17 , *p; q = p incorrect. q et p deux pointeurs sur des
p = &x; q =p; types différents
4 int x, *p; &x = p; &x = p incorrect. &x n’est pas une variable
(lvalue) et par conséquent ne peut pas figurer à
gauche d’une affectation.
7
20/12/2020
Pointeurs et tableaux
En C, il existe une relation très étroite entre tableaux et
pointeurs. Ainsi, chaque opération avec des indices de tableaux
peut aussi être exprimée à l’aide de pointeurs. En effet, le nom
d’un tableau représente l’adresse de son premier élément :
Tableau à une dimension (int T[N]) :
⁃ le nom T du tableau est un pointeur constant sur le premier élément (1er
entier) du tableau
⁃ T et &T[0] contiennent l’adresse du premier élément (1er entier) du
tableau.
Tableau à deux dimensions (int T[N][M]) :
⁃ le nom T est un pointeur constant sur le premier tableau d’entiers
⁃ T[i] est un pointeur constant sur le premier élément (1er entier) du
ième tableau.
⁃ T et T[0] contiennent la même adresse mais leur manipulation n’est
pas la même puisqu’ils ne représentent pas le même type de pointeur.
Pointeurs et tableaux
Adressage et accès aux composantes d’un tableau à une
dimension
En déclarant un tableau A de type int (int A[N]) et un pointeur p sur
des variables entières (int *p), l’instruction p = A crée une liaison
entre le pointeur p et le tableau A en mettent dans p l’adresse du
premier élément de A (de même p = &A[0]).
A partir du moment où p = A, la manipulation du tableau A peut se
faire par le biais du pointeur p. En effet
p pointe sur A[0] *p désigne A[0]
p+1 pointe sur A[1] *(p+1) désigne A[1]
…
p+(N-1) pointe sur A[N-1] *(p+N-1) désigne A[N-1]
8
20/12/2020
Exemple
Lecture et Affichage d’un tableau matérialisé par un pointeur
#include <stdio.h>
#define N 10
main(){
float t[N] , *pt ;
int i ;
printf("Entrez %d entiers\n", N) ;
pt = &t[0] ; // ou pt = t
for (i = 0 ; i<N ; i++)
scanf("%f", pt+i); // pt+i pointe sur t[i]
printf("\n Tableau lu : \n") ;
for (i = 0 ; i<N ; i++)
printf(" %f ", *(pt+i)); //*(pt+i) équivalente à t[i]
}
Exemple
Lecture et Affichage d’un tableau matérialisé par un pointeur
/* Autre Solution sans déclarer la variable i */
#include <stdio.h>
#define N 10
main(){
float T[N] , *pt ;
printf("Entrez %d entiers\n", N) ;
for (pt = T ; pt<T+N ; pt++)
scanf("%f", pt);
printf("\nTableau lu : \n") ;
for (pt = T ; pt<T+N ; pt++)
printf("%7.2f", *pt) ;
}
9
20/12/2020
Pointeurs et tableaux à deux dimensions
En déclarant une matrice A de type int (int A[M][N]) et un pointeur p sur des
variables entières (int *p),
l’instruction p = A[0] crée une liaison entre le pointeur p et la matrice A en
mettent dans p l’adresse du premier élément de la première ligne de la matrice A
( p = &A[0][0]). A partir du moment où p = A[0], la manipulation de la matrice
A peut se faire par le biais du pointeur p. En effet :
p pointe sur A[0][0] et * p désigne A[0][0]
p+1 pointe sur A[0][1] et * ( p + 1 ) désigne A[0][1]
…
p+N pointe sur A[1][0] et * ( p + N ) désigne A[1][0]
p+N+1 pointe sur A[1][1] et * ( p + N + 1 ) désigne A[1][1]
…
p+i*N pointe sur A[i][0] et * ( p + i*N ) désigne A[i][0]
...
p+i*N+j pointe sur A[i][j] et * ( p + i*N +j) désigne A[i][j]
...
p + M*N-1 pointe sur A[M-1][N-1] et * ( p + M * N -1 ) désigne A[M-1][N-1]
Exemple
Lecture et Affichage d’une matrice matérialisée par un pointeur
#define M 4
#define N 10
main(){
short A[M][N] ;
short *pt ;
int i, j ;
/* lecture d’une matrice */
pt = &A[0][0] ; /* ou bien pt = A[0] ; */
for (i = 0 ; i<M ; i++){
printf("\t ligne n° %d\n", i+1);
for (j = 0 ; j<N ; j++)
scanf("%d", pt + i * N + j );
}
/* affichage d’une matrice */
for (i = 0 ; i<M ; i++) {
for (j = 0 ; j<N ; j++)
printf("%d", *( pt + i * N + j ) ) ;
printf("\n") ;
}
}
10
20/12/2020
Arithmétiques des pointeurs
Affectation par un pointeur sur le même type :
• Soient P1 et P2 deux pointeurs sur le même type de
données.
• L’affectation : P1 = P2 ; fait pointer P1 sur le même objet
que P2.
Addition et soustraction d’un nombre entier :
• Si P pointe sur l’élément A[i] d’un tableau, alors :
• P+n pointe sur A[i+n] et P-n pointe sur A[i-n]
Arithmétiques des pointeurs
Incrémentation et décrémentation d’un pointeur :
• Si P pointe sur l’élément A[i] d’un tableau, alors après
l’instruction :
- P++ ; P pointe sur A[i+1]
- P += n ; P pointe sur A[i+n]
- P-- ; P pointe sur A[i-1]
- P -= n ; P pointe sur A[i-n]
Comparaison de deux pointeurs :
• On peut comparer deux pointeurs de même type par : <, >, <=,
>=, == ou !=
• La comparaison de deux pointeurs qui pointent dans le même
tableau est équivalente à la comparaison des indices
correspondants.
11
20/12/2020
Autres déclarations des pointeurs
En C, il existe d’autres déclarations des pointeurs. En effet :
Tableau de pointeurs :
• int *Tab[20] ;
• déclare un tableau Tab de 20 pointeurs d’entiers.
Pointeur de tableaux :
• int (*pt)[30] ;
• déclare un pointeur pt sur des tableaux de 30 composantes.
Pointeur de pointeurs :
• int **pt ;
• déclare un pointeur pt qui pointe sur des pointeurs d’entiers.
Allocation dynamique
La déclaration d’un tableau définit un tableau statique (il
possède un nombre figé d’emplacements). Il y a donc un
gaspillage d’espace mémoire en réservant toujours
l’espace maximal prévisible.
Il serait souhaitable que l’allocation de la mémoire dépend
du nombre d’éléments à saisir. Ce nombre ne sera connu
qu’à l’exécution :
c’est l’allocation dynamique.
12
20/12/2020
Allocation dynamique
Fonction d’allocation dynamique de la mémoire
• Bibliothèque <stdlib.h>
void *malloc(taille) ; //allocation d’un bloc
void *calloc(taille, sizeof(type)) ; //allocation & initialisation d’un bloc
void *realloc(void *, taille); //modification de la taille d’un bloc
void free(void *) ; //libération d’un bloc
• Chacune des fonctions malloc, calloc ou realloc, prend une
zone d’une taille donnée dans l’espace mémoire libre réservé
pour le programme (appelé tas ou heap) et renvoie:
⁃ L'adresse (non typée, void ∗) du bloc alloué s'il y a suffisamment
de mémoire disponible.
⁃ La valeur NULL en cas d'erreur (pas assez de mémoire libre).
Fonctions malloc et free
malloc
• <pointeur> = <type> malloc(<taille>);
- <type> est un type pointeur définissant la variable pointé
par <pointeur>
- <taille> est la taille, en octets, de la zone mémoire à allouer
dynamiquement, <taille> est du type unsigned int, donc on
ne peut pas réserver plus de 65536 octets à la fois
• La fonction malloc retourne l’adresse du premier octet de
la zone mémoire allouée. En cas d’échec, elle retourne
NULL.
13
20/12/2020
Fonctions malloc et free
free
• Si on n’a plus besoin d’un bloc de mémoire réservé
dynamiquement par malloc, alors on peut le libérer à
l’aide de la fonction free.
• free(<pointeur>);
• Libère le bloc de mémoire désigné par le pointeur
<pointeur>
Allocation dynamique - exemple
Saisie et Affichage d’un tableau:
#include <stdio.h>
#include <stdlib.h>
main(){
short *pt;
int N , i;
printf("Entrez la taille N du tableau \n") ;
scanf("%d", &N) ;
pt = ( short * ) malloc( N * sizeof( short ) );
if (pt == NULL)
printf("Mémoire non disponible") ;
14
20/12/2020
Allocation dynamique - exemple
Saisie et Affichage d’un tableau (suite):
……
printf("Saisie du tableau : ");
for ( i = 0 ; i < N; i++)
scanf("%d", pt + i ) ;
printf("Affichage du tableau ") ;
for ( i= 0 ; i < N; i++)
printf("%d\t", *( pt + i ) ) ;
free( pt );
……
}
Gestion dynamique des tableaux
Exemple:
/* Déclaration d'un pointeur sur des entiers p
et de la taille du tableau N */
int* p=NULL;
int N;
/* Saisie de la taille du tableau par
l'utilisateur */
scanf ("%d",&N);
/* Allocation dynamique de la zone mémoire
occupée par le tableau */
p = (int*)malloc (N*sizeof(int));
15