0% ont trouvé ce document utile (0 vote)
39 vues30 pages

Cours Pointeur

Transféré par

bentaherdaly123
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PPSX, PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
39 vues30 pages

Cours Pointeur

Transféré par

bentaherdaly123
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PPSX, PDF, TXT ou lisez en ligne sur Scribd

Atelier de Programmation Procédurale 1

(Langage C)
Niveaux: Info 1
Dr Nafaa Haffar

Année universitaire:
2023/2024
Les pointeurs
Les pointeurs 1

Présentation de la problématique

 Problématique
Imaginez par exemple que vous souhaitez résider dans une ville pendant une période donnée!

Chercher un logement (une maison)

Comment trouver un logement ?

Que Faire ?????

Contacter une agence immobilière (location de maisons)?

L’agence va vous affecter à un logement qui est


adapté à vos besoins.
Les pointeurs 2

Présentation de la problématique

 Problématique
Si nous projetons cette problématique sur le mécanisme de gestion de la mémoire?

La Maison ⇔ L’espace mémoire où la variable sera stockée


(Adresse)

L’agence ⇔ Le système d’exploitation qui va gérer la mémoire

La ville ⇔ La mémoire (RAM)

La personne ⇔ La variable qu’on veut stocker


Les pointeurs 3

1) Introduction

 Une variable correspond à un emplacement en mémoire (adresse) où se trouve une valeur.


 Accéder à sa valeur en lecture et en écriture
int n ;
n = 5 ; // écriture de la valeur de n (initialisation de n)
printf("n vaut %d", n) ; // lecture de la valeur de n
 Accéder à son adresse en lecture seulement, car l'adresse (l'emplacement mémoire) est choisie par le système.
printf("L'adresse de n est %d", &n) ; // lecture de l'adresse de n

mémoire
Adresse 1 (0XAABA) Résultat (Affichage)

Adresse 2 (0XAABC) 5
… n vaut 5
L'adresse de n est 0XAABC

Adresse n (0XAABN)
Les pointeurs 4

1) Introduction

► Pointeur ?

 Un pointeur est une donnée qui représente l'adresse d'une variable.

 Pour que l'adresse stockée dans un pointeur soit exploitable :

 il faut connaître le type de l'information qui se situe au niveau de cette adresse.

 Pour cette raison, un pointeur doit être toujours associé à un type donné.

Comment gérer les pointeurs ?


Les pointeurs 5

2) Déclaration

 Formellement la syntaxe de déclaration d'un pointeur est la suivante :


 Syntaxe :
Type* identificateur ; // La variable identificateur est un pointeur vers une valeur de type Type.

 Exemple :
char* c ; // déclaration d’un pointeur nommé (c) qui pointe vers une donnée de type caractère.
int* i ; // déclaration d’un pointeur nommé (i) qui pointe vers une donnée de type entier.
float* d ; // déclaration d’un pointeur nommé (d) qui pointe vers une donnée de type réel double.

pointeurs mémoire
c
A

Rq : Les pointeurs occupent tous la même taille


i 12
en mémoire indépendamment de la taille du 14.65
type de l'objet pointé d
Les pointeurs 6

2) Déclaration

 Il est possible de faire une déclaration multiple de pointeurs à travers la syntaxe suivante :

 Syntaxe :
Type* identificateur1 , … , * identificateur n ; // Les variables identificateurs1 .. n sont des pointeurs vers des valeurs de type
Type.

 Exemple :

int * p1 , *p2 ; // déclaration des pointeurs nommés (p1, p2) qui pointent vers des données de type entier.

int *p1 , p2 ; // déclaration d’un pointeur nommé (p1) qui pointe vers une donnée de type entier et une variable (p2) de type entier.

int p1 , *p2 ; // déclaration d’une variable (p1) de type entier et un pointeur nommé (p2) qui pointe vers une donnée de type entier.
Les pointeurs 7

3) Initialisation

 La valeur initiale d’un pointeur est l'adresse d'une donnée dans la mémoire.
 L'initialisation d'un pointeur ne peut s'effectuer que si la variable est déjà existante dans la mémoire.
 L'initialisation d'un pointeur se fait à travers la syntaxe suivante :
 Syntaxe :
Type* identificateur = &variable ; // initialisation d’un pointeur (identificateur) par l’adresse de la variable de type type.

 Exemple :
int i ; // déclaration d’une variable nommée (i) de type entier
int * pi = &i ; // initialisation d’un pointeur nommé (pi) avec l’adresse de la variable (i).

Rq : Le pointeur ne doit pointer que vers une variable de même type, sinon le compilateur affiche alors une erreur lors de la compilation
char c ; // déclaration d’une variable nommée (c) de type caractère
int * p = &c ; // Erreur! Puisque p n'est pas un pointeur vers un caractère (p est un pointeur vers un entier )
Les pointeurs 8

4) Affectation des pointeurs et conversion

► Affectation
 Une variable de type pointeur peut obtenir sa valeur non seulement par une initialisation, mais également par une opération d'affectation.
 Syntaxe
Type* identificateur1 , *identificateur2 ; // Les variables identificateurs1 .. n sont des pointeurs vers des valeurs de type type.
identificateur1 = identificateur2 ; // Affectation de la valeur de l’identificateurs2 vers identificateurs1
 Exemple
int i ; // déclaration d’une variable nommée (i) de type entier
int * pi1, *pi2 ; // déclaration des pointeurs nommés (pi1, pi2) qui pointent vers des données de type entier.
pi1 =&i; // pi1 contient l'adresse i
pi2 = pi1; // pi2 contient l'adresse i
Rq : Les pointeurs doivent être de même type
pi1 0XAABC
= i
pi2 0XAABC

mémoire
Les pointeurs 9

4) Affectation des pointeurs et conversion

► Conversion
 Contrairement aux autres types de variables, il n'existe aucune conversion implicite d'un type pointeur vers un autre type pointeur.
 Le seul moyen pour faire des conversions entre types pointeurs est le casting (Conversion forcée).
 Syntaxe :
Type1* identificateur1 ; Type2* identificateur2 ; // identificateur1 et identificateur2 sont des pointeurs sur Type1 et Type2 .
identificateur1 = (Type1*) identificateur2 ; // Conversion forcée de type de l’identificateur2 (Type2) vers Type1

 Exemple :
int i ; // déclaration d’une variable nommée (i) de type entier
int * pi = &i ; // initialisation d’un pointeur nommé (pi) de type (int) par l’adresse de la variable (i).
unsigned int *piu ; // déclaration d’un pointeur nommé (piu) qui pointe vers une donnée de type entier non signé.
pui = pi; // Erreur  Type incompatible (affectation d’un entier à un entier non signé)
pui=(unsigned int *) pi; // Valide 
// Correction de l’incompatibilité en fonçant la conversion de type de pointeur (pi) en un entier non
signé
Les pointeurs 10

5) Accès indirect aux variables

 Il est possible d'accéder à une variable à travers un pointeur qui pointe vers cette variable.
 Il suffit d'utiliser pour cela l'opérateur * de la manière suivante :
 Syntaxe :
Type NomVariable, *NomPointeur ; // Déclaration d’une variable et un pointeur de type Type.
NomPointeur = &NomVariable ; // Affectation de l’adresse de la variable au pointeur

Alors on peut accéder à une variable de deux manières : NomVariable ⇔ * NomPointeur


 Exemple :
int i ; // déclaration d’une variable nommée (i) de type entier
int * pi ; // déclaration d’un pointeur nommé (pi) de type entier
i = 5; // initialisation de la variable i à 5.
pi = &i; // pi contient l'adresse de i  L’utilisation de (*pi) désigne d'une manière indirecte le contenu de i
printf("i vaut %d", i) ; // i vaut 5
printf("i vaut %d", *pi) ; // *pi vaut 5
Les pointeurs 11

5) Accès indirect aux variables

 L'opérateur * permet de manipuler en lecture et en écriture la valeur d'une variable à travers un pointeur.
 Exemple :
int i ; // déclaration d’une variable nommée (i) de type entier
int * pi ; // déclaration d’un pointeur nommé (pi) de type entier
Habituellement, on passerait l'adresse de la
pi = &i ; // pi contient l'adresse de i
variable en utilisant &i, mais dans ce cas, pi
scanf("%d",pi) ; // Saisie (valeur saisie est 3)
représente déjà l'adresse de i.
printf("i vaut %d", *pi) ; // Affichage (valeur affichée est 3)

mémoire pointeur
Résultat (Affichage)
Adresse 1 (0XAABA)
0XAABC pi
Adresse 2 (0XAABC) 3 printf("%d", *pi) ; // 3
… printf("%p", pi) // 0XAABC
Adresse n (0XAABN)
Les pointeurs 12

6) Incrémentation de pointeurs et addition

► Incrémentation
 L'incrémentation d'un pointeur donne l'adresse située à sizeof (TYPE) octets à partir de la valeur courante du pointeur.
 TYPE étant le type de la variable pointée par le pointeur.

 Exemple

int i ; // déclaration d’une variable nommée (i) de type entier

int * pi ; // déclaration d’un pointeur nommé (pi) de type entier

pi = &i ; // pi contient l'adresse de i Supposons que pi vaut 1200

pi++; // incrémente p de 4 octets ⇔ pi = pi +1


 pi vaut 1200 + 1 * sizeof(int) = 1200 + 1*4 = 1204
Les pointeurs 13

6) Incrémentation de pointeurs et addition

► Addition
 L'addition entre un pointeur et un entier (n) donne l'adresse située à n*sizeof (TYPE) à partir de la valeur
courante du pointeur.

Exemple
Supposons que i vaut 1200
int* i; int* j ; // déclaration des pointeurs nommés (i, j) qui pointent vers des données de type entier.
j vaut 1200 + 10 * sizeof(int)
int k ; // déclaration d’une variable nommée (k) de type entier
= 1200 + 10*4 = 1240
i = &k ; // i contient l'adresse de k
⇔ j = j +1
j = i+ 10 ; // j contient la valeur de i +10  j vaut 1240 + 1 * sizeof(int)
= 1240 + 1*4 = 1244
j++ ; // incrémentation de j

Rq : l'addition n'est définie qu'entre un pointeur et un entier ( addition entre deux pointeurs | entre un pointeur et un nombre en virgule )
Les pointeurs 14

7) Décrémentation de pointeurs et soustraction

► Décrémentation
 La décrémentation d'un pointeur donne l'adresse située à - sizeof (TYPE) octets à partir de la valeur courante du pointeur.
 TYPE étant le type de la variable pointée par le pointeur.

 Exemple :

int i ; // déclaration d’une variable nommée (i) de type entier

int * pi ; // déclaration d’un pointeur nommé (pi) de type entier Supposons que pi vaut 1200

pi = &i ; // pi contient l'adresse de i ⇔ pi = pi - 1


 pi vaut 1200 - 1 * sizeof(int)
pi--; // décrémente pi de 4 octets
= 1200 - 1*4 = 1196
Les pointeurs 15

7) Décrémentation de pointeurs et soustraction

► Soustraction
 La soustraction d’un entier (n) à partir d’un pointeur donne l'adresse située à -n*sizeof (TYPE).
 Contrairement à l'addition, on peut soustraire d'un pointeur non seulement un entier, mais aussi un autre pointeur de même type.
Exemple :
int* i; int* j ; // déclaration des pointeurs nommés (i, j) qui pointent vers des données de type entier.
int Tab [5] ; // déclaration d’un tableau nommé (Tab) de type entier
i = &Tab [1] ; // i contient l'adresse de l’élément 1 de tableau (Tab)
j = &Tab [4] ; // j contient l'adresse de l’élément 4 de tableau (Tab)
printf("i vaut %d", j-i) ; //donne 3 ⇔ la soustraction entre deux pointeurs donne le nombre des éléments

i 1200 j 1212

0 1 2 3 4
Tab

4 octets
Les pointeurs 16

8) Comparaison entre pointeurs

 La comparaison de pointeurs est possible, mais seulement entre pointeurs de même type.

Exemple 1
int* p1; int* p2 ; // déclaration des pointeurs nommés (p1, p2) qui pointent vers des données de type entier.
if (p1 == p2) // Tester si les pointeurs (p1, p2) pointent vers la même donnée ou non
printf("Les deux pointeurs pointent vers la même donnée") ;
else
printf("Les deux pointeurs pointent vers des données différentes") ;

Exemple 2
int* p1; int* p2 ; // déclaration des pointeurs nommés (p1, p2) qui pointent vers des données de type entier.
if (p2 > p1) // Tester si le pointeur (p2) contient une adresse plus grande à celle de pointeur (p1)
printf("Le pointeur p2 contient une adresse plus grande que le pointeur p1") ;
Les pointeurs 17

9) Pointeurs génériques

► Définition

 Il existe en C des pointeurs particuliers appelés « pointeurs génériques ».

 Ces types de pointeurs peuvent pointer vers des données de n’importe quel type (entier, réel, …).

 Les pointeurs génériques s'avèrent utiles si :

 l'adresse d'une zone mémoire doit être enregistrée, mais que le type de donnée n'est pas encore établi.

 on ne pas se fixer dans l'immédiat sur un quelconque type de données.


Les pointeurs 18

9) Pointeurs génériques

► Déclaration
 Les pointeurs génériques sont déclarés à l'aide du type : void*
 La déclaration se fait comme suit :
 Syntaxe :
void* identificateur ; // Déclaration d’un pointeur générique.

 Exemple :

int i ; double d; // Déclaration de deux variables i de type entier et d de type double


void * vp; // pointeur générique
vp=&i; // vp contient l'adresse de la variable i
vp=&d; // vp contient l'adresse de la variable d
Les pointeurs 19

9) Pointeurs génériques

► Restrictions
 Un pointeur générique ne peut pas servir pour faire des accès indirects aux contenus des variables.

 Exemple :

int i ; // Déclaration d’une variable i de type entier


void * vp; // pointeur générique
Le compilateur ne peut pas déterminer le pas de déplacement (octets?)
vp=&i; // vp contient l'adresse de la variable i
ce qui peut entraîner un calcul incorrect ou provoquer une erreur
vp++; // incrémentation de vp

vp 1200 vp 1201
char
(char*) vp++
Solution
vp 1204 (int*) vp++
int
Conversion explicite (casting) de
vp 1208 vp comme suit (double*) vp++
double
Les pointeurs 20

9) Pointeurs génériques

► Affectation d'un pointeur générique à un pointeur d'un autre type


Avec certains compilateurs, le code peut compiler et
 Également, l'affectation du contenu d'un pointeur générique (void*) à un pointeur (type*) doit obligatoirement passer
s'exécuter sans erreur même s'il viole les règles du
par le casting.
langage C.
 Exemple :
Cependant, cela ne signifie pas que le code est sûr ou
int i ; // déclaration d’une variable de type entier correct
int* pi1; int* pi2 ; // déclaration des pointeurs nommés (pi1, pi2) qui pointent vers des données
 peut conduire de type entier.
à des résultats imprévisibles ou à des
pi1 = &i ; // pi1 contient l'adresse de i erreurs difficilement détectable
void * pg; // pg est un pointeur générique
pg = pi1; // valide! pg reçoit la valeur du pointeur pi
pi2 = pg ; // erreur ! Affectation n’est pas possible d’un pointeur générique à un pointeur de type int  (casting)
pi2 = (int*) pg ; // valide ! Affectation possible d’un pointeur générique à un pointeur de type int avec le casting
printf("%d", *pi2); // valide ! opération possible, car le type de pi2 est connu int*
pi2++; // valide ! opération possible, car le type de pi2 est connu int*
Rq : La conversion implicite entre un pointeur générique et un pointeur type se fait comme suit: Type* vers void* v | void* vers Type* ×  casting
Les pointeurs 21

10) Pointeurs et tableaux

► Définition
 En C, le nom d'un tableau est considéré comme une constante d'adresse définissant l'emplacement de début à partir
duquel vont se succéder les éléments du tableau.
 Lorsqu'il est employé seul, l'identificateur d'un tableau est considéré comme un pointeur constant.

 Étant donnée la déclaration suivante :


int T[5] ;

T 0 1 2 3 4

 Dans ce cas T est considéré comme un pointeur constant.


Les pointeurs 22

10) Pointeurs et tableaux

► Notation d'accès aux éléments d'un tableau


 Étant donnée la déclaration suivante :
int T[5] ;
Nous avons les notations équivalentes suivantes :
T ⇔ &T[0] || T+1 ⇔ &T[1] || T+i ⇔ &T[i] // équivalence d’adresses
*T ⇔ T[0] || *(T+1) ⇔ T[1] || *(T+i) ⇔ T[i ] // équivalence des accès

 Exemples : Remplissage Remplissage avec pointeur


classique
int T[5], i ; int T[5], i ;
for(i=0 ; i<5 ; i++){ for(i=0 ; i<5 ; i++){
T[i] = i; *(T+i) = i ;
} }
Les pointeurs 23

10) Pointeurs et tableaux

► Notation d'accès aux éléments d'un tableau


 Le nom du tableau ne peut pas être utilisé pour faire le parcours des éléments.
 Étant donnée la déclaration suivante :
int T[10] ;
 L'incrémentation T++ est incorrecte.
 T est dans ce cas considéré comme un pointeur constant
 Si T est utilisé, on risque de perdre l’index du tableau
 Solution ? l'utilisation d'une variable d’un autre pointeur pour faire ce parcours
 Exemple

p 1200 … p 1216
int T[5], i ,*p ;
for(i=0, p=T ; i<5 ; i++, p++) 0 1 2 3 4
*p= i ; T 0 1 2 3 4
Les pointeurs 24

11) Pointeurs et constantes

► Pointeur en lecture seule

 Un pointeur en lecture seule peut pointer sur n'importe quelle variable.

 Il ne permet toutefois que l'accès en lecture à la variable pointée.

 Toute tentative de modification (accès en écriture) est signalée comme erreur.


Les pointeurs 25

11) Pointeurs et constantes

► Pointeur en lecture seule


 La déclaration d’un pointeur en lecture seule se fait à travers la syntaxe suivante :
 Syntaxe
const Type* NomPointeur ;
 Exemple
int i=5, j=10; // déclaration de deux variables (i, j) de type entier
const int* p = &i; // valide ! p pointeur contient l’adresse de i
printf("Valeur pointée : %d ", *p); // Affichage de la valeur pointé par p qui est 5
p = &j; // valide ! p pointeur contient l’adresse de j
printf("Valeur pointée : %d ", *p); // Affichage de la valeur pointé par p qui est 10
*p = 8; // erreur! (tentative de modification)  p est un pointeur en lecture seule
printf("Valeur pointée : %d ", *p); // Affichage de la valeur pointé par p qui est 10

error: assignment of read-only location ‘*p’


Les pointeurs 26

11) Pointeurs et constantes

► Pointeur en lecture seule


 Indication :
 On ne peut pas affecter l'adresse d'un objet constant à un pointeur sur un type non constant
 une telle affectation pourrait permettre de modifier cet objet par l'intermédiaire du pointeur.
 Exemple :
const int i=5 ; // déclaration d’une constante (i) de type entier
const int* p = &i ; // valide ! p pointeur qui contient l’adresse de i
int* q = &i ; // avertissement ! affectation de l'adresse de la constante (i) à un pointeur sur un type non constant (q)
warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]

p mémoire
const
0XAABC

q
0XAABC 5 i const
0XAABC ….
Les pointeurs 27

11) Pointeurs et constantes

► Pointeur constant
 Un pointeur constant est un pointeur qui ne peut pointer que sur une seule variable.
 Le contenu de la variable pointée n'est pas nécessairement constant et peut par conséquent être modifié.
 La déclaration d’un tel type de pointeur se fait de la manière suivante :
 Syntaxe :
Type* const NomPointeur ;
 Exemple :
int i=5, j=10; // déclaration de deux variables (i,j) de type entier
int* const p = &i ; // valide ! p pointeur contient l’adresse de i
printf("Valeur pointée : %d ", *p); // Affichage de la valeur pointé par p qui est 5
i=8; // Modification de la valeur de la variable i
printf("Valeur pointée : %d ", *p); // Affichage de la valeur pointé par p qui est 8
p = &j ; // erreur ! Le pointeur p ne peut pointer que sur une seule variable qui est (i)
printf("Valeur pointée : %d ", *p); // Affichage de la valeur pointé par p qui est 8
Les pointeurs 27

Exercice d’application A B C P1 P2

Initialisation 1 2 3 / /
► Pointeur constant
p1=&A; 1 2 3 &A /
 Soit le programme suivant:
p2=&C; 1 2 3 &A &C
*p1=(*p2)++; 3 2 4 &A &C
int main() { p2=&B;
int A=1; *p1-=*p2; p1=p2; 3 2 4 &C &C
int B=2; ++*p2; p2=&B; 3 2 4 &C &B
int C=3; *p1*=*p2; *p1-=*p2; 3 2 2 (4-2) &C &B
int *p1,*p2; A=++*p2**p1;
++*p2; 3 3 (2+1) 2 &C &B
p1=&A; p1=&A;
*p1*=*p2; 2 3 6 (2*3) &C &B
p2=&C; *p2=*p1/=*p2;
A=++*p2**p1; 24 (4*6) 4 (3+1) 6 &C &B
*p1=(*p2)++; return 0;
p1=p2; } p1=&A; 24 4 6 &A &B
*p2=*p1/=*p2; 6 (24/4) 6 6 &A &B

Rq : Res = ++x  Incrémentation + Affectation


Res = x++  Affectation +Incrémentation

Vous aimerez peut-être aussi