Programmation C
Chapitre 5:
Les pointeurs
S.ELMANSOURI
2023-2024
Adresse mémoire
▪ Les informations, octets, int’s, chaînes sont stockées en mémoire.
→Sont enregistrées à un certain endroit de la mémoire : adresse.
▪ Adresse ≈ index de l’info dans le grand «tableau » de la mémoire.
Adresses et pointeurs
▪ Une adresse est une information comme une autre.
→ Besoin de la mémoriser, manipuler et d’accéder à son contenu.
▪ Pointeur : variable dont la valeur est une adresse.
▪ Variable pointée : variable dont l’adresse est contenue dans un pointeur.
▪ La variable pointée peut être de n’importe quel type : type simple, comme on l’a
déjà vu, mais aussi types complexes, ou même autres pointeurs.
→ Un pointeur est une adresse avec un type associé : le type des données
stockées à cette adresse.
Adresses et pointeurs
▪ Exemple:
Adresses et pointeurs
▪ Chaque variable possède une adresse.
▪ Obtenir l’adresse d’une variable : opérateur préfixe &.
▪ Accéder au contenu d’une adresse : opérateur préfixe *.
▪ & et * ont des rôles symétriques : *(&a) est la même chose que a.
Déclaration et initialisation
▪ On déclare un pointeur comme une variable classique, à l’exception du fait qu’on
fait précéder le nom de la variable de * :
▪ Exemple: char c = 4;
long x = 1234;
long *y;
▪ Pour utiliser le pointeur, il faut le faire pointer sur une adresse. Pour cela, on utilise
une expression appliquant l’opérateur & à une variable.
▪ Exemple, on peut faire pointer y sur x :
y = &x;
▪ On peut aussi utiliser un autre pointeur qui a déjà été initialisé.
▪ Exemple: on peut déclarer un nouveau pointeur z pointant sur la même adresse
que y, donc : x.
long *z = y;
Accès
▪ Une fois qu’un pointeur a été initialisé, l’opérateur * permet d’accéder à la valeur
contenue à l’adresse pointée.
▪ Exemple: afficher le contenu de la variable x en passant par les pointeurs y ou z :
//Les trois instructions affichent la même valeur 1234.
printf("contenu de x via x : %d",x);
printf("contenu de x via y : %d",*y);
printf("contenu de x via z : %d",*z);
▪ De la même façon, si on modifie la valeur contenue à l’adresse pointée, on modifie
la variable classique située à cette adresse.
▪ Exemple:
*y = 1000;
Exemple de manipulations de pointeurs
▪ Chaque case mémoire a une adresse.
▪ Par habitude et simplicité, adresses écrites en hexadécimal.
Exemple de manipulations de pointeurs
Exemple de manipulations de pointeurs
▪ c et d contiennent les adresses de b et a.
▪ Ils «pointent » vers b et a.
Exemple de manipulations de pointeurs
▪ On suit le pointeur dans c et on stocke 4 dans la case mémoire
correspondante → dans b.
Exemple de manipulations de pointeurs
▪ À gauche du “=” : case mémoire où ira le résultat.
▪ À droite du “=” : on évalue, i.e. on suit le pointeur c et on regarde ce qui est
dans la case.
Exemple de manipulations de pointeurs
▪ e pointe vers c.
Exemple de manipulations de pointeurs
▪ Double étoile : on suit deux pointeurs à la suite i.e. pointeur sur un pointeur.
Exemple de manipulations de pointeurs
▪ Deux pointeurs peuvent contenir la même adresse.
▪ On peut faire un test d’égalité dessus (c == d).
▪ On peut aussi comparer les contenus (*c == *d).
Allocation et libération
de mémoire
Statique versus dynamique
▪ Allocation statique : effectuée à la compilation.
▪ Ex : variables entières déclarées, tableaux de taille fixe :
▪ int v = 42 ;
▪ char s[10] ;
▪ Allocation statique locale : automatiquement libérée en fin de fonction.
▪ Allocation dynamique: On demande de la mémoire selon les besoins à
l’exécution.
▪ Zone allouée retournée désignée par son adresse de début.
Allocation dynamique
▪ La fonction malloc (memory allocation) permet d’allouer dynamiquement une
certaine quantité de mémoire. Son en-tête est la suivante :
void* malloc(int size)
➢ Argument : nombre d’octets demandés.
➢ Résultat : adresse du début de la zone octroyée.
➢ void* : pointeur sans type, implicitement compatible avec autres types de
pointeurs.
▪ Exemple: on veut allouer une zone de 3 entiers short
short *p;
p = (short*)malloc(3*sizeof(short));
//sizeof renvoie la taille en octets d’une donnée du type spécifié en paramètre
Allocation dynamique
▪ Si mémoire non disponible, résultat = pointeur NULL
→ Toujours tester la réussite de l’allocation !
▪ Exemple
short *p;
p = (short*)malloc(3*sizeof(short));
if(p == NULL)
// traitement de l’erreur
else
// traitement normal
Libération de mémoire
▪ La fonction free (memory allocation) permet permet de libérer une zone de
mémoire qui avait été allouée par malloc :
void free(void *p)
➢ Il suffit de passer en paramètre l’adresse de la zone à libérer. Ce paramètre
est de type void*, donc il peut s’agit de n’importe quel type de pointeur.
▪ Exemple:
free(p);
Pointeurs et tableaux
Pointeurs et tableaux à une dimension
▪ Tout tableau en C est en fait un pointeur constant.
int tab[10];
➢ tab est un pointeur constant (non modifiable) dont la valeur est l’adresse du
premier élément du tableau, i.e. tab ~&tab[0]
➢ On peut donc utiliser un pointeur initialisé a tab pour parcourir les éléments
du tableau.
▪ On accède à l’élément d’indice i du tableau tab grâce a l’operateur d’indexation
[], par l’expression tab[i].
▪ Cet operateur d’indexation peut en fait s’appliquer a tout objet p de type pointeur.
Il est lié à l’opérateur d’indirection * par la formule p[i] = *(p + i)
Pointeurs et tableaux à une dimension
▪ Exemple:
#define N 5
int tab[5] = {1, 2, 6, 0, 7};
main()
{
int i;
int *p;
p = tab;
for (i = 0; i < N; i++)
{
printf(" %d \n",*p);
//equivalent à:
//printf(" %d \n", p[i]);
p++;
}
}
Pointeurs et tableaux à une dimension
▪ La manipulation de tableaux, et non de pointeurs, possède certains inconvénients:
➢ on ne peut pas créer de tableaux dont la taille est une variable du programme,
➢ on ne peut pas créer de tableaux bidimensionnels dont les lignes n’ont pas
toutes le même nombre d’éléments.
▪ Ces opérations deviennent possibles dès que l’on manipule des pointeurs alloués
dynamiquement.
▪ Exemple: Pour créer un tableau d’entiers à n éléments où n est une variable du
programme: #include <stdlib.h>
main()
{
int n;
int *tab;
...
tab = (int*)malloc(n * sizeof(int));
...
free(tab);
}
Pointeurs et tableaux à plusieurs dimensions
▪ Un tableau à deux dimensions est, par définition, un tableau de tableaux. Il s’agit
donc en fait d’un pointeur vers un pointeur. Considérons le tableau à deux
dimensions défini par : int tab[M][N];
➢ tab est un pointeur, qui pointe vers un objet lui-même de type pointeur d’entier.
➢ tab a une valeur constante égale à l’adresse du premier élément du tableau,
&tab[0][0].
➢ De même tab[i], pour i entre 0 et M-1, est un pointeur constant vers un objet
de type entier, qui est le premier élément de la ligne d’indice i.
➢ tab[i] a donc une valeur constante qui est égale à &tab[i][0].
▪ On déclare un pointeur qui pointe sur un objet de type type * (deux dimensions)
de la même manière qu’un pointeur
type **nom-du-pointeur;
Pointeurs et tableaux à plusieurs dimensions
▪ Exemple:
main()
{
int k, n;
int **tab;
tab = (int**)malloc(k * sizeof(int*));
for (i = 0; i < k; i++)
tab[i] = (int*)malloc(n * sizeof(int));
....
for (i = 0; i < k; i++)
free(tab[i]);
free(tab);
}
Pointeurs et tableaux à plusieurs dimensions
▪ Contrairement aux tableaux à deux dimensions, on peut choisir des tailles
différentes pour chacune des lignes tab[i].
▪ Exemple: si l’on veut que tab[i] contienne exactement i+1 éléments, on écrit:
for (i = 0; i < k; i++)
tab[i] = (int*)malloc((i + 1) * sizeof(int));
Pointeurs et chaînes de caractères
Pointeurs et chaînes de caractères
▪ Une chaîne de caractères était un tableau à une dimension d’objets de type char,
se terminant par le caractère nul ’\0’
▪ On peut donc manipuler toute chaîne de caractères à l’aide d’un pointeur sur un
objet de type char.
▪ Exemple: La fonction strlen, définie dans la librairie standard string.h, donnant
la longueur d’une chaîne de caractères procède de manière suivante :
#include <stdio.h>
main()
{
int i;
char *chaine;
chaine = "Ceci est une chaine";
for (i = 0; *chaine != ’\0’; i++)
chaine++;
printf("nombre de caracteres = %d\n",i);
}
Pointeurs et chaînes de caractères
▪ Exemple: Créer une chaîne correspondant à la concaténation de deux chaînes de
caractères
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main()
{ int i;
char *chaine1, *chaine2, *res, *p;
chaine1 = "chaine ";
chaine2 = "de caracteres";
res = (char*)malloc((strlen(chaine1) + strlen(chaine2)) * sizeof(char));
p = res; //utilisation d’un pointeur intermediaire p
/*sinon on aurait “perdu” la référence sur le premier caractère de la
Chaîne res.*/
for (i = 0; i < strlen(chaine1); i++)
*p++ = chaine1[i];
for (i = 0; i < strlen(chaine2); i++)
*p++ = chaine2[i];
printf("%s\n",res);
}
Questions?