Chapitre 2 : pointeurs et variables dynamique Université de Batna 2 Algorithmique et structures de données
CH2 : Les pointeurs et les variables dynamiques
1. Introduction :
Nous allons apprendre dans ce chapitre à faire la distinction entre une variable statique et une variable dynamique. Ceci
nécessite qu’un étudiant doive comprendre la gestion dynamique de la mémoire ainsi que la représentation de chaque type de
variables (le nombre d’octets, le moment d’allocation, le temps d’existence, etc...).
a. Important :
Supposons qu’au cours de ce chapitre : un langage de programmation X représente les variables de type caractères sur 1
octet , les entiers sur 2 octets, le réel sur 4 octets et les adresses sur 1 seule octet.
Chaque variable est représenté par son nom (identificateur : var x), son adresse (ex en décimale : #1, #200, #3953) et son
contenu (x=5).
2. Définition d’un pointeur :
On appel un pointeur une variable dont sa valeur est une adresse mémoire. Cette dernière peut être une adresse d’une variable
de type entier, réel, caractère, etc...
Ex1 :
Nom de la variable Adresse Contenu
Var x :integer ; ...
..... #2003
x=5 ; #2002
.....
#2001
P = #100
#2000
...
Dans cet exemple le pointeur P contient une adresse d’une variable de
type entier. #101 5
X ou (P^) ou (*P) #100
...
#2
P #1 #100
#0
Ex2 :
Var y:real; Nom de la variable Adresse Contenu
..... ...
y=5 ; #2003 5
..... #2002
P = #2000 #2001
Y ou (P^) ou (*P) #2000
Dans cet exemple le pointeur P contient une adresse d’une variable de
...
type réel.
#101 5
x #100
...
#2
P #1 #100
#0
Conclusion 1 :
On appel type de base d’un pointeur : le type de la variable pointé par le pointeur (ex1 : type de base = entier.) (ex2 : type de
base = réel).
Un pointeur P pointe sur une variable dynamique notée P↑ ! (P^ en pascale) (*P en langage C).
M. bada 1
Chapitre 2 : pointeurs et variables dynamique Université de Batna 2 Algorithmique et structures de données
3. Etapes de déploiement d’un pointeur :
Nom de la variable Adresse Contenu
a. Etape 1 : déclaration : ...
#2003
En pascal : var Nom : ^Type ; #2002
Ex : #2001
var P1 : ^integer ; #2000
P2 : ^real ; ...
#101
#100
En C : Type* Nom ; ...
Ex : P2 #2 NULL
int* P1 ; ou int *P1 ; P1 #1 NULL
double* P2 ; ou double *P2 ; #0 Réservée
Cette écriture permet de créer un espace mémoire pour les variables P1 et P2 seulement, dont le contenu est égal à
NULL (NIL en pascal).
NULL = #0 (00...000 en binaire) ! (cette adresse n’est pas utilisé par le système).
b. Etape 2 : allocation d’espace mémoire (au moment d’exécution) :
Nom de la variable Adresse Contenu
...
#2003
#2002
#2001
#2000
...
#101 0
#100
...
P2 #2 NULL
P1 #1 #100
#0 Réservée
En pascal : New (P1) ;
Le paramètre de cette procédure doit être de type pointeur.
Cette écriture permet d’ :
• Allouer un espace mémoire de 2 octets pour une variable de type entier.
• Affecter l’adresse de l’espace réservé à la variable P1.
En C : P1= (int*) malloc( sizeof(int) ) ;
Cette instruction est composée de deux fonctions : sizeof ( Type ) et malloc ( Nbr_D_Octets ).
• Sizeof (Type) : permet de récupérer le nombre d’octets utilisé pour représenté un type donné (int, double,
float,...) dans une mémoire « dépend de la machine : intel, DEC, ...»
Dans cet exemple : Sizeof( int) =2 octets.
• Malloc (Nbr_D_Octets) : permet d’allouer un espace mémoire de N octets. Cette fonction retourne une
adresse mémoire, qui sera affecté à la variable P1 par la suite.
Dans cet exemple : P1= Malloc (2 octets ) = #100
M. bada 2
Chapitre 2 : pointeurs et variables dynamique Université de Batna 2 Algorithmique et structures de données
c. Etape 3 : utilisation des 2 variables (P et *P):
Apres l’allocation d’espace mémoire dans l’étape N2, on peut bien remarqué qu’il existe 2 variables :
P de type pointeur qui contient l’adresse #100.
Et un espace mémoire de 2 octets réservé pour un entier, et qui n’a pas du nom prédéfinit (pas
d’identificateur)
Question : comment peut on utiliser la 2eme variable de type entier ?
Réponse : Pour utiliser la nouvelle variable (entier), on peut utiliser l’identificateur : P^ (en pascal) ou (*P) (en C) .
En Pascal :
P^ := 5 ;
Nom de la variable Adresse Contenu
En C :
...
*P = 5 ; #2003
#2002
#2001
#2000
...
#101 0
P^ ou (*P) #100
...
P2 #2 NULL
P1 #1 #100
#0 Réservée
d. Etape 4 : Libération d’espace mémoire:
Nom de la variable Adresse Contenu
Lorsque une variable n’est plus nécessaire n’est plus ...
nécessaire dans un programme, il est possible de récupérer #2003
l’espace occupé par celle-ci et l’utiliser pour d’autres. #2002
#2001
En pascal : #2000
Dispose (P) ; ...
#101
En C : #100
Free (P) ; ...
P2 #2 NULL
P1 #1 NULL
#0 Réservée
4. Définition d’une variable dynamique :
Une variable dynamique est une variable qu’on alloue à n’importe quel moment du programme, et qu’on peut libérer à la
fon de son utilisation.
Ex : var X : interger ; ! X est une variable statique (variable qui existe jusqu'à la fin du programme)
var P : ^ integer ; ! P est une variable statique (variable qui existe jusqu'à la fin du programme)
mais P^ ou (*P) est une variable dynamique
M. bada 3
Chapitre 2 : pointeurs et variables dynamique Université de Batna 2 Algorithmique et structures de données
5. Pointeur vers un enregistrement (structure) : « déclaration et utilisation) :
Exemple : Nom de la Adresse Contenu
#include <stdio.h> variable
#include <stdlib.h> ...
#include <string.h> #122
---------------------------------------------- #121
struct etudiant (*P).Nom ...
{ ...
int N; #102
char Nom [20];
#101
};
(*P).N #100
----------------------------------------------
...
int main (void)
{ #2
---------------------------------------------- P #1 #100
etudiant* P; #0 Réservée
----------------------------------------------
P= (etudiant*) malloc( sizeof(etudiant) );
----------------------------------------------
//affectation
(*P).N=77; // il faut tjr mettre la variable dynamique entre ().
strcpy( (*P).nom, "XXX" ); // pour l’affectation des chaines de caractères, il faut utiliser
la fonction strcpy
//lecture à partir d’un clavier
scanf ("%i", &(*P).N); // pour la lecture à partir d’un clavier, le passage des paramètres
//est par adresse ! utilisation de & avant le nom de la variable
scanf ("%s", &(*P).Nom);
//affichage
printf("la valeur de n = %i",(*p).N);
printf("la valeur de n = %s",(*p).Nom);
return 0 ;
Exercice 1 (langage C):
Supposons que le NBR d’étudiants dans une classe ne dépassera jamais 50.
Sachant qu’un étudiant est représenté par : son N d’inscription, son nom [max = 20 caractères] et son prénom
[max = 20 caractères].
a) Proposer une structure de données en utilisant seulement des variables statiques qui permettent de gérer les
étudiants d’une classe donnée.
b) Proposer une autre structure de données en utilisant des variables dynamiques qui permettent de gérer les
étudiants d’une classe donnée.
c) Calculer l’espace mémoire utilisé dans les 2 solutions pour une classe de 20 étudiants. Que peut on
conclure ?
M. bada 4
Chapitre 2 : pointeurs et variables dynamique Université de Batna 2 Algorithmique et structures de données
Solution :
a) Structure de données statiques :
----------------------------------------------
struct etudiant
{
int N;
char nom [20];
char prenom [20];
};
----------------------------------------------
etudiant T[50];
----------------------------------------------
b) Structure de données dynamiques:
----------------------------------------------
struct etudiant
{
int N;
char nom [20];
char prenom [20];
};
----------------------------------------------
etudiant* T[50];
----------------------------------------------
for (int i=0 ; i<50 ; i++)
{
T[i]= (etudiant*) malloc( sizeof(etudiant) );
}
----------------------------------------------
(*T[1]).N=44; // il faut tjr mettre la variable dynamique entre ().
strcpy( (*T[1]).nom, "XXX" ); // pour l’affectation des chaines de caractères, il faut utiliser
la fonction strcpy
c) Espace mémoire utilisé :
Pour la 1ere solution : Espace = (2+20+20)*50 = 2100 octets
Pour la 2eme solution : Espace= 50 + (2+20+20)*20 = 890 octets
Dans la programmation statique, toutes ces variables occupent de l’espace en mémoire
du début jusqu’à la fin de l’exécution du programme ! gaspillage et une mauvaise
gestion de la mémoire.
Les variables dynamiques, capables d’être créées et supprimées a tout moment ! une
meilleure optimisation de l’espace mémoire.
M. bada 5
Chapitre 2 : pointeurs et variables dynamique Université de Batna 2 Algorithmique et structures de données
6. Operations sur les pointeurs (machine réel):
Sur une machine intel, le compilateur C représente le type int sur 4 octets et pas 2 octets.
a. l’addition/ soustraction des pointeurs :
Si nous déclarons un pointeur (int* P1) ; comme ci-dessous
! alors l’expression (P1 + 1) va retourner un pointeur sur un « int » et sa valeur est l’adresse de l’entier suivant
l’entier pointé par P1.
#include <stdio.h>
int main (void ){
int *P1;
printf ("Avant : le pointeur P1 contient la valeur %p'",P1);
P1 = P1 +1; // l'incrimentation d'adresse par 4 octets "type entier"
printf ("Apres : le pointeur P1 contient la valeur %p'",P1);
Résultat : Avant : le pointeur P1 contient la valeur 0000000000000000 (en hexadécimal).
0: parce qu’on n’a pas encore allouer un espace mémoire pour la variable dynamique pointé par P1.
Apres : le pointeur P1 contient la valeur 0000000000000003
Conclusion :
On pourra relever que la différence entre l’adresse de (P1) et (P1 + 1) est de 4. Cette valeur
correspond au nombre d’octets d’un int (entier).
b. Comparaison entres les pointeurs :
Le langage C nous permet de comparer si deux pointeurs sont égaux (opérateur ==) ou non égaux ( !=) entre eux.
On peut aussi comparer un pointeur avec la valeur NULL (en majuscule).
Les comparaisons d’ordre (<, <=, >, >=) sont aussi autorisés avec les pointeurs.
c. Pointer une variable simple qui existe déjà :
En langage C, on peut affecter à P une adresse d’une variable qui existe déjà en utilisant le caractère spéciale & (et
commerciale):
Ex :
int x ;
int * P ;
x=6 ;
P=&x ; (P= l’adresse de X)
X et *P contiennent la même valeur = 6 ( x " *P )
M. bada 6
Chapitre 2 : pointeurs et variables dynamique Université de Batna 2 Algorithmique et structures de données
d. Pointer un tableau :
Soit un tableau TAB de int et un pointeur P sur un int déclarés comme suit :
int tab[10], *P;
Pour accéder au nième élément du tableau, on fait appel à TAB[i].
Maintenant faisons pointer P sur le premier élément du tableau tab :
P = &TAB[0]; /* ou P = &TAB; ce qui est équivalent */
Pour accéder au nième élément du tableau, il suffit d’écrire :
TAB[i] ou *(P + i)
Exemple :
int *P,
tab[5]={4,2,7,8,6};
printf("%d", tab[3]); /* affiche 8*/
P = &tab;
printf("%d", *(pn+3)); /* affiche 8 aussi */
7. Conclusion :
Dans la programmation statique, toutes ces variables occupent de l’espace en mémoire du début jusqu’à la fin de l’exécution
du programme ! gaspillage et une mauvaise gestion de la mémoire.
Les variables dynamiques, capables d’être créées et supprimées a tout moment ! une meilleure optimisation de l’espace
mémoire.
L’utilisation des pointeurs est très fortement liée aux structures de données dynamique tels que : les listes, les arbres
etc...
M. bada 7