Chapitre 3 Les Pointeurs
Chapitre 3 Les Pointeurs
Chapitre 3 Les Pointeurs
Les Pointeurs
Zied Trifa
trifa.zied@gmail.com
Plan du chapitre
1) Notion de pointeur
2) Pointeurs et tableaux
3) Pointeurs et chaînes de caractères
4) Allocation dynamique de la mémoire
2
1) Notion de pointeur
3
1) Notion de pointeur
• Exercice : Supposons que je veuille écrire une fonction à laquelle on envoie un
nombre de minutes. Celle-ci renverrait le nombre d'heures et minutes
correspondantes :
– si on envoie 45, la fonction renvoie 0 heure et 45 minutes ;
– si on envoie 60, la fonction renvoie 1 heure et 0 minutes ;
– si on envoie 90, la fonction renvoie 1 heure et 30 minutes.
#include <stdio.h>
void decoupeMinutes(int heures, int minutes) /* déclaration de la fonction */
{
heures = minutes / 60;
minutes = minutes % 60;
} Résultat :
int main() /* programme principal */ 0 heures et 90 minutes
{
int heures = 0, minutes = 90;
decoupeMinutes(heures, minutes); /* appel de la fonction */
printf (”%d heures et %d minutes”, heures, minutes);
return 0;
}
4
1) Notion de pointeur
• Explication :
• En fait, quand vous « envoyez » une variable à une fonction, une copie de la variable est
réalisée.
• Ainsi, la variable heures dans la fonction decoupeMinutes n'est pas la même que celle de la
fonction main ! C'est simplement une copie !
• Votre fonction decoupeMinutes fait son job. À l'intérieur de decoupeMinutes, les variables
heures et minutes ont les bonnes valeurs : 1 et 30.
• Comme vous avez appris dans les chapitres précédents, toutes les variables créées dans une
fonction sont détruites à la fin de cette fonction.
• Vos copies de heures et de minutes sont donc supprimées. On retourne ensuite à la fonction
main, dans laquelle vos variables heures et minutes valent toujours 0 et 90. C'est un échec !
5
1) Notion de pointeur
• Adresse et valeur :
• Quand vous créez une variable age de type int par exemple, en tapant ça :
• int age =10;
• Votre programme demande au système d'exploitation la permission d'utiliser un peu de
mémoire. Ce dernier répond en indiquant à quelle adresse en mémoire il vous laisse le droit
d'inscrire votre nombre.
• Revenons à notre variable age. La valeur 10 a été inscrite quelque part en mémoire, disons
par exemple à l'adresse n° 4655.
• Donc :
– age : désigne la valeur de la variable ;
– &age : désigne l'adresse de la variable.
6
1) Notion de pointeur
• Utiliser des pointeurs :
• Maintenant, nous allons apprendre à créer des variables faites pour contenir des adresses : ce
sont justement ce qu'on appelle des pointeurs.
• Pour créer une variable de type pointeur, on doit rajouter le symbole * devant le nom de la
variable.
– int *monPointeur;
• Il est important d'initialiser dès le début ses variables, en leur donnant la valeur 0 par
exemple. C'est encore plus important de le faire avec les pointeurs !
– int *monPointeur = null;
• la valeur du pointeur est faite pour contenir une adresse. L'adresse… d'une autre variable.
– int age = 10;
– int *pointeurSurAge = &age;
7
1) Notion de pointeur
• Utiliser des pointeurs :
• Essayons de voir ce que contient le pointeur à l'aide d'un printf :
• Comment faire pour demander à avoir la valeur de la variable se trouvant à l'adresse indiquée
dans pointeurSurAge ? Il faut placer le symbole * devant le nom du pointeur
8
1) Notion de pointeur
Exemple 1 :
Soit A une variable contenant la valeur 10, B une variable contenant la valeur 50 et P un
pointeur non initialisé :
Exemple 2 :
Soit P = &x; les instructions suivantes sont équivalentes :
y=*P+1 à y=x+1
*P=*P+1 à x=x+1
(*P)++ à x++
9
TD N°3 --- Exercice 1 - Enoncé
Écrire un programme se servant de la fonction triplePointeur afin de multiplier par 3 la
valeur d’un nombre.
10
TD N°3 --- Exercice 1 - Solution
#include <stdio.h>
11
TD N°3 --- Exercice 2 - Enoncé
Supposons que je veuille écrire une fonction à laquelle on envoie un nombre de
minutes. Celle-ci renverrait le nombre d'heures et minutes correspondantes :
Exemple :
si on envoie 45, la fonction renvoie 0 heure et 45 minutes ;
si on envoie 60, la fonction renvoie 1 heure et 0 minutes ;
si on envoie 90, la fonction renvoie 1 heure et 30 minutes.
12
TD N°3 --- Exercice 2 - Solution
#include <stdio.h>
void decoupeMinutes(int *heures, int *minutes) /* déclaration de la fonction */
{
*heures = *minutes / 60;
*minutes = *minutes % 60;
}
int main() /* programme principal */
{
int heures = 0, minutes = 90;
decoupeMinutes(&heures, &minutes); /* appel de la fonction */
printf (”%d heures et %d minutes”, heures, minutes);
return 0;
}
13
2) Pointeurs et tableaux
• L’importance des pointeurs en C :
• En C, les pointeurs jouent un rôle primordial dans la définition de fonctions.
• Les pointeurs sont le seul moyen de changer le contenu de variables déclarées dans d'autres
fonctions.
14
2) Pointeurs et tableaux
Exemple :
En déclarant un tableau A de type int et un pointeur P sur int :
int A [10];
int *P;
L’instruction P = A; est équivalente à P = &A[0];
15
TD N°3 --- Exercice 3 - Enoncé
Soit le programme suivant :
#include<stdio.h>
int main ()
{
int T[10] = {-3, 4, 0, -7, 3, 8, 0, -1, 4, -9};
int POS [10];
int i, j; /* indices courants dans T et POS */
for (j=0; i=0; i<10; i++)
if (T[i] > 0)
{
POS[j] = T[i];
j++;
}
return 0;
}
17
TD N°3 --- Exercice 4 - Enoncé
Ecrire un programme qui lit deux tableaux A et B et leurs dimensions N et M au clavier
et qui ajoute les éléments de B à la fin de A.
Utiliser le formalisme pointeur à chaque fois que cela est possible.
18
TD N°3 --- Exercice 4 – Solution (1/3)
#include<stdio.h>
int main ()
{ /* déclaration */
int A[100], B[50]; /* tableaux */
int n, m; /* dimensions des tableaux */
int i; /* indice courant */
/* saisie des données */
Do
{
printf("Taille du tableau A (N<=50) =");
scanf("%d", &n);
} while (n<0 || n>50);
/* Copie de B à la fin de A */
for(i=0; i<m; i++)
*(A+n+i) = *(B+i);
/* Nouvelle dimension de A */
n += m;
/* Edition du résultat */
printf("Tableau résultat A : \n");
for(i=0; i<n; i++)
printf("%d :\n", *(A+i));
return 0;
}
21
TD N°3 --- Exercice 5 - Enoncé
Écrire un programme qui permet de remplir un tableau d’entiers de dimension N (tel
que N≤50) et d’afficher les nombres impairs.
22
TD N°3 --- Exercice 5 – Solution (1/2)
#include<stdio.h>
int TailleTableau() /* fonction qui retourne la taille du tableau */
{
int Taille;
Do
{
printf("Taille du tableau (N<=50) =");
scanf("%d", &Taille);
} while (Taille<0 || Taille>50);
return Taille;
}
void Remplissage(int Taille, int *TAB) /* fonction remplissage du tableau */
{
int i;
for(i=0; i<Taille; i++)
{
printf("Elément n° %d :", i+1);
scanf("%d", &TAB[i]);
}
} 23
TD N°3 --- Exercice 5 – Solution (2/2)
void Affichage(int Taille, int TAB[])
{
int i;
for(i=0;i<Taille;i++)
{
if(TAB[i] % 2 !=0)
printf("% d ", TAB[i]);
}
}
int main()
{
int N;
int T[50];
N=TailleTableau();
Remplissage(N,&T);
Affichage(N,T);
return 0;
}
24
TD N°3 --- Exercice 6 - Enoncé
Ecrire un programme qui lit un entier X et un tableau A du type int au clavier et élimine
toutes les occurrences de X dans A en tassant les éléments restants. Le programme
utilisera les pointeurs P1 et P2 pour parcourir le tableau.
25
TD N°3 --- Exercice 6 – Solution (1/2)
#include <stdio.h>
int main()
{ /* Déclarations */
int A[50]; /* tableau donné */
int N; /* dimension du tableau */
int X; /* valeur à éliminer */
int *P1, *P2; /* pointeurs d'aide */
/* Saisie des données */
printf("Dimension du tableau (max.50) : ");
scanf("%d", &N );
for (P1=A; P1<A+N; P1++)
{
printf("Elément %d : ", P1-A); scanf("%d", P1);
}
printf("Introduire l'élément X à éliminer du tableau : ");
scanf("%d", &X );
26
TD N°3 --- Exercice 6 – Solution (2/2)
/* Affichage du tableau */
for (P1=A; P1<A+N; P1++)
printf("%d \n ", *P1);
/* Effacer toutes les occurrences de X et comprimer : */
/* Copier tous les éléments de P1 vers P2 et augmenter */
/* P2 pour tous les éléments différents de X. */
for (P1=P2=A; P1<A+N; P1++)
{ *P2 = *P1;
if (*P2 != X) P2++;
}
/* Nouvelle dimension de A */
N = P2-A;
/* Edition du résultat */
for (P1=A; P1<A+N; P1++)
printf("%d \n", *P1);
return 0;
}
27
3) Pointeurs et chaînes de caractères
• un pointeur sur char peut pointer sur un caractère isolé ou sur les
éléments d'un tableau de caractères.
• Un pointeur sur char peut en plus contenir l'adresse d'une chaîne de
caractères constante et il peut même être initialisé avec une telle
adresse.
Exemple 1 :
char *c; c = "Ceci est une chaîne de caractères constante";
28
3) Pointeurs et chaînes de caractères
• Il existe une différence importante entre les deux déclarations :
– char A[] = ‘’Bonjour !’’; /* tableau */
– char *B = ‘’Bonjour !’’; /* un pointeur */
• B est un pointeur qui est initialisé de façon à ce qu’il pointe sur une
chaîne de caractères constante stockée quelque part en mémoire. Le
pointeur peut être modifié et pointer sur autre chose. La chaîne peut
être lue, copiée ou affichée, mais pas modifiée.
29
3) Pointeurs et chaînes de caractères
• Conclusions :
– Utilisons des tableaux de caractères pour déclarer les chaînes de caractères que
nous voulons modifier.
– Utilisons des pointeurs sur char pour manipuler des chaînes de caractères
constantes (dont le contenu ne change pas).
30
3) Pointeurs et chaînes de caractères
Exemple (1/2) :
Reprenons l'exemple de la fonction strcpy, qui copie la chaîne CH2 vers CH1. Les
deux chaînes sont les arguments de la fonction et elles sont déclarées comme pointeurs
sur char. La première version de strcpy est écrite entièrement à l'aide du formalisme
tableau:
void strcpy(char *CH1, char *CH2)
{
int I=0;
while ((CH1[I]=CH2[I]) != '\0')
I++;
}
nous pourrions remplacer simplement la notation tableau[I] par *(tableau + I),
ce qui conduirait au programme:
void strcpy(char *CH1, char *CH2)
{
int I=0;
while ((*(CH1+I)=*(CH2+I)) != '\0')
I++;
}
31
3) Pointeurs et chaînes de caractères
Exemple (2/2) :
Un 'véritable' avantage se laisse gagner en calculant directement avec les pointeurs CH1
et CH2 :
void strcpy(char *CH1, char *CH2)
{ while ((*CH1=*CH2) != '\0')
{
CH1++;
CH2++;
}
}
un vrai professionnel en C escaladerait les 'simplifications' jusqu'à obtenir :
void strcpy(char *CH1, char *CH2)
{
while (*CH1++ = *CH2++)
;
}
32
TD N°3 --- Exercice 7 - Enoncé
Ecrire un programme qui lit une chaîne de caractères CH et détermine la longueur de la
chaîne à l'aide d'un pointeur P.
Le programme n'utilisera pas de variables numériques.
33
TD N°3 --- Exercice 7 – Solution
#include <stdio.h>
int main()
{ /* Déclarations */
char CH[101]; /* chaîne donnée */
char *P; /* pointeur d'aide */
/* Saisie des données */
printf("Entrez une ligne de texte (max.100 caractères) :\n");
gets(CH);
/* Placer P à la fin de la chaîne */
for (P=CH; *P; P++) ;
/* Affichage du résultat */
printf("La chaîne \"%s\" est formée de %d caractères.\n", CH, P-CH);
return 0;
}
34
TD N°3 --- Exercice 8 - Enoncé
Ecrire un programme qui lit un caractère C et une chaîne de caractères CH au clavier.
Ensuite toutes les occurrences de C dans CH seront éliminées. Le reste des caractères
dans CH sera tassé à l'aide d'un pointeur et de la fonction strcpy.
35
TD N°3 --- Exercice 8 – Solution (1/2)
#include <stdio.h>
#include <string.h>
int main()
{ /* Déclarations */
char CH[101]; /* chaîne donnée */
char C; /* lettre à éliminer */
char *P; /* pointeur d'aide dans CH */
/* Saisie des données */
printf("Entrez une ligne de texte (max.100 caractères) :\n");
gets(CH);
printf("Entrez le caractère à éliminer (suivi de Enter) :\n");
C=getchar();
36
TD N°3 --- Exercice 8 – Solution (2/2)
/* Comprimer la chaîne à l'aide de strcpy */
P=CH;
while (*P)
{
if (*P==C)
strcpy(P, P+1);
else P++;
}
/* Affichage du résultat */
printf("Chaîne résultat : \"%s\"\n", CH);
return 0;
}
37
TD N°3 --- Exercice 9 - Enoncé
Ecrire un programme qui lit une chaîne de caractères CH et détermine le nombre de
mots contenus dans la chaîne. Utiliser un pointeur P, une variable logique, la fonction
isspace et une variable numérique N qui contiendra le nombre des mots.
38
TD N°3 --- Exercice 9 – Solution (1/2)
#include <stdio.h>
#include <ctype.h>
int main()
{ /* Déclarations */
char CH[101]; /* chaîne donnée */
char *P; /* pointeur d'aide */
int N; /* nombre des mots */
int DANS_MOT; /* indicateur logique: vrai si P pointe à l'intérieur un mot */
/* Saisie des données */
printf("Entrez une ligne de texte (max.100 caractères) :\n");
gets(CH);
39
TD N°3 --- Exercice 9 – Solution (2/2)
/* Compter les mots */
N=0;
DANS_MOT=0;
for (P=CH; *P; P++)
if (isspace(*P))
DANS_MOT=0;
else if (!DANS_MOT)
{
DANS_MOT=1;
N++;
}
/* Affichage du résultat (pour perfectionnistes) */
printf("La chaîne \"%s\" \n est formée de %d mot%c.\n", CH, N, (N==1)?' ':'s');
return 0;
}
40
4) Allocation dynamique de la mémoire
Problème :
Pour les 10 pointeurs, nous avons besoin de 10*p octets. Ce nombre est connu dès le
départ et les octets sont réservés automatiquement. Il nous est cependant impossible de
prévoir à l'avance le nombre d'octets à réserver pour les phrases elles-mêmes qui seront
introduites lors de l'exécution du programme ...
41
4) Allocation dynamique de la mémoire
• Solution : Allocation dynamique
• La réservation de la mémoire pour les 10 phrases peut donc seulement
se faire pendant l'exécution du programme. Nous parlons dans ce cas
de l'allocation dynamique de la mémoire.
La fonction « malloc » retourne un pointeur de type char * pointant vers un objet
de taille nombre_octets octets.
Pour initialiser des pointeurs vers des objets qui ne sont pas de type char, il faut
convertir le type de la sortie de la fonction malloc à l'aide d'un cast.
L'argument nombre-octets est souvent donné à l'aide de la fonction sizeof ( )
qui renvoie le nombre d'octets utilisés pour stocker un objet.
#include <stdio.h>
#include <stdlib.h>
void main()
{ int i = 3;
int *p;
p = (int*) malloc (sizeof (int));
*p = i;
printf ("*p = %d\n",*p);
} 42
4) Allocation dynamique de la mémoire
• NB : Allocation dynamique
• Lorsqu'on n'a plus besoin de l'espace-mémoire alloué dynamiquement
(c'est-à-dire quand on n'utilise plus le pointeur p), il faut libérer cette
place en mémoire.
• Ceci se fait à l'aide de l'instruction free qui a pour syntaxe:
– free (nom-du-pointeur);
43
4) Allocation dynamique de la mémoire
Exemple 1 :
#include <stdio.h>
#include <stdlib.h>
void main()
{
char *p;
p = (char *) malloc (50);
strcpy (p, "ISAEG")
printf ("*p = %s\n",*p);
free (p);
}
44
4) Allocation dynamique de la mémoire
Exemple 2 :
#include <stdio.h>
#include <stdlib.h>
void main()
{
int *p, i, s;
p = (int *) malloc (sizeof (int)*20);
for(i=0; i<20; i++)
scanf("%d", &p[i]); /* scanf("%d", p+i); */
for(i=0, s=0; i<20; i++)
s += *(p+i);
45
TD N°3 --- Exercice 10 - Enoncé
Ecrire une fonction qui fait la somme de deux vecteurs d’entier.
Exemple :
V1 : 3 4 2 5 6
V2 : 12 2 14 2 2
V3 : 15 6 16 7 8
46
TD N°3 --- Exercice 10 – Solution (1/2)
Solution 1 :
void somme1 (int *v1, int *v2, int *v3, int taille)
{
int i;
for(i=0; i<taille; i++)
*(v3+i) = *(v1+i) + *(v2+i);
}
Solution 2 :
int * somme2 (int *v1, int *v2, int taille)
{
int i;
static int v3 [200];
for(i=0; i<taille; i++)
*(v3+i) = *(v1+i) + *(v2+i);
return v3;
}
47
TD N°3 --- Exercice 10 – Solution (2/2)
Solution 3 :
int * somme2 (int *v1, int *v2, int taille)
{
int i, *v3;
v3 = (int *) malloc (sizeof(int)*taille);
for(i=0; i<taille; i++)
*(v3+i) = *(v1+i) + *(v2+i);
return v3;
}
48