Les pointeurs Enormément utilisé en C/C++ ! Pourquoi? A quoi ça sert? Manipuler directement la mémoire de l’ordinateur. Déclarer des tableaux de taille variable. Copier directement des tableaux ou des chaînes de caractères. Utile pour le passage de paramètres. … Bases de la programmation en C++
La notion d’adresse 65775417 123 65775416 3.2132 65775415 ‘M’ 65775414 La mémoire de l’ordinateur peut être vu comme une série de cases. Ces cases sont repérées par des adresses. Dans ces cases sont stockées les valeurs des variables (ou des instructions). Adresses valeurs Pour accéder à la valeur d’une variable ( i.e au contenu de la case mémoire) Il faut connaître son emplacement (c’est l’adresse de la variable) 65775417 123 65775416 3.2132 65775415 ‘M’ 65775414 ‘i’ 65775413 ‘a 65775412 ‘m’ Lorsqu’on utilise une variable: Le compilateur manipule son adresse pour y accéder. Cette adresse est alors inconnue du programmeur Bases de la programmation en C++
Qu’est ce qu’un pointeur ? Un pointeur est une variable qui contient l’adresse d’une variable. Le stockage en mémoire de la variable (char, int, etc…) est alors sous la responsabilité du programmeur. On dit que le pointeur pointe sur l’objet pointé. Le type du pointeur est construit à partir du type de l’objet pointé. Exemple Déclaration d’un pointeur Nom de la variable Type de l’objet pointé int * Var ; On a déclaré une variable de type « Pointeur sur entier » Cette variable contient l’adresse d’une case mémoire contenant un entier Bases de la programmation en C++
Opérateur concernant les pointeurs Comment accéder à la variable contenue dans un pointeur ? Comment récupérer l’adresse d’une variable ? & Opérateur d’adresse * Opérateur d’indirection -> Membre d’une structure pointée Soit a une variable et p un pointeur: &a désigne l’adresse de la variable a *p désigne la variable pointée par p Bases de la programmation en C++
Exemple de déclaration et d’utilisation int i=0; /* Déclare une variable entière. */ int *pi; /* Déclare un pointeur sur un entier. */ pi=&i; /* Initialise le pointeur avec l’adresse de cette variable. */ *pi = *pi+1; /* Effectue un calcul sur la variable pointée par pi, c’est-à-dire sur i lui-même, puisque pi contient l’adresse de i. */ /* À ce stade, i ne vaut plus 0, mais 1. */ int *px, y, z; // px est un pointeur sur entier // y et z sont des entiers struct Client { int Age; }; Client Paul; Client *PJean = & Paul; PJean ->Age = 35; /* On aurait pu écrire (* PJean ).Age=35; */ Bases de la programmation en C++
Attention ! Il faut s’assurer que les pointeur que l’on manipule sont bien initialisé! Il doivent contenir l’adresse d’une variable valide. Accéder à la variable d’un pointeur non initialisé revient à: Ecrire ou lirer, dans la mémoire à un endroit aléatoire. Plantage à l’exécution du programme. En général on initialise les pointeurs à la déclaration. On peut les initialisé comme pointeur NULL. int * Var; // pour le compilateur Var pointe sur quelque chose (n’importe quoi) int * Var = NULL; // le compilateur sait que Var ne pointe sur rien Bases de la programmation en C++
Comment initialiser un pointeur Affectation à une variable existante. int i = 2; int * P; P = &i; 65775417 65775416 65775415 65775414 65775413 65775412 2 i = *P ? P 65775415 XXXXX P Bases de la programmation en C++
Comment initialiser un pointeur Affectation à une variable existante. int i,j; // i, j variables de type int int * pi, *pj; // pi et pj, variables de type « pointeurs vers int » pi=&i; // le pointeur est initialisé à &i, il pointe à l’adresse &i, donc sur i *pi=2; // accès à l’élément pointé, i est mis à 2 (*pi)++; // incrémente i qui passe à 3 pj=pi; // pj pointe au même endroit que pi (sur i) int t[10]; // tableau de 10 int pi = &t[2]; // pi pointe sur la case qui contient la valeur T[2] *(pi + 1) = 0; // T[3] est mis à 0 (les éléments d’un tableau se suivent en mémoire) int i=2, j , k ; int * p1, * p2; p1 = &i; // p1 contient l’adresse de i *p1 = i; // inutile! Car pi=&i donc automatiquement *pi=i p2 = k; // attention l’adresse p2 prend une valeur non défini //(donc p2 pointe n’importe ou) *p2 = 3; // on écrit quelque chose à un endroit inconnu ! Plantage! « Bases de la programmation en C++
Comment initialiser un pointeur Allocation dynamique. On réserve la place pour la variable pointée. Syntaxe: Nom_Variable = new type ; int * P; P = new int ; 65775417 65775416 65775415 65775414 65775413 65775412 *P ? P 65775415 XXXXX P Bases de la programmation en C++
Comment initialiser un pointeur Allocation dynamique Il faut penser à libérer la mémoire après utilisation ! Syntaxe: delete Nom_Variable ; Même si on déclare un pointeur dans une fonction (local), il doit être libéré ! char * C; // C est un pointeur qui pointe sur n’importe quoi C = new char ; // Allocation d’un espace pouvant stocker un caractère // C pointe sur cet espace double * nb ; Nb = new double ; // allocation d’un espace pour stocker un double double * D; D = new double ; *D = 12.34; delete D ; // libération de l’espace occupé par le double Bases de la programmation en C++
Arithmétique des pointeurs Les opérateur ++, --, +, = ….. Sont définis pour les pointeurs, mais attention, ils s’appliquent aux adresses. int * P; …. //allocation P ++ ; P = P – 2 ; int * Q = P + 3; 65775417 123 65775416 12 65775415 156 65775414 3 65775413 9 65775412 456 Q P Bases de la programmation en C++
Pointeur et tableau Un tableau est en fait un pointeur! int tableau[3]; int * P = tableau ; Tableau [2] = 5 ; *(tableau+1) = 4; P[0]=1; 65775417 65775416 65775415 65775414 65775413 65775412 5 P 4 1 tableau Bases de la programmation en C++
Autres exemples avec des tableaux Les allocation dynamique Possibilité de créer un tableau de taille non constante ! Syntaxe: Type * NomTab; NomTab = new Type [Taille]; // allocation Delete [ ] NomTab; // destruction Int tab[3]; int * Ptab, x; // Ptab pointeur sur un int Ptab = & tab [0]; // Ptab pointe sur le début du tableau // équivalent à Ptab = tab x = *Ptab; // équivalent à x = tab[0] *(Ptab+ i) = 3; // équivalent à tab[i]=3, *(tab + i) = 3 , Ptab[i]=3 int n = 3; int tab[n]; // INTERDIT int * Ptab = new int [n]; // OK …. delete [ ] Ptab; // désallocation du pointeur Bases de la programmation en C++
Différence pointeur / tableau Un tableau est un pointeur constant ! On peut donc transtyper un pointeur en tableau mais pas l’inverse! L’affectation est possible entre pointeur. int tab[3]; // déclaration d’un tableau de 3 éléments int * Ptab = new int [3]; // déclaration d’un pointeur avec allocation pour 3 éléments Ptab ++; // OK tab++; // ERREUR tab est constant Ptab = tab; // ok les deux pointeur Ptab et tab pointe au même endroit (sur tab[0]) Tab = Ptab ; // ERREUR tab est constant int * tab1 = new int [3]; // déclaration d’un pointeur avec allocation pour 3 éléments int * tab2 = new int [3]; ; Tab2 = tab1 ; //attention Tab2 pointe maintenant au même endroit que Tab1 char * chaine; chaine = "coucou"; Bases de la programmation en C++
Affectation de pointeur 12 3 65 int * tab1 = new int [3]; int * tab2 = new int [3]; // remplissage tab2 = tab1; delete [] tab2; delete [] tab1; 65775417 65775416 65775415 65775414 65775413 65775412 789 31 756 tab2 tab2 = tab1 Erreur Bases de la programmation en C++
Pointeur et fonction Un nouveau type de passage de paramètres Le passage de paramètre par pointeur (ou par variable). // passage par valeur void Fonction1(int a) { a = 2; return; } // passage par référence void Fonction2(int & a) { a = 2; return; } // passage par pointeur void Fonction3(int * a) { *a = 2; return; } int x=0; int y=0; int z=0; Fonction1(x); // la valeur de x ne change pas (passage par valeur) Fonction2(y); // la valeur de y passe à 1 (passage par référence) Fonction3(&z); // la valeur de z passe à 1 (passage par pointeur) // on passe l’adresse de z à la, fonction 3 qui réclame un pointeur en paramètre Bases de la programmation en C++
Exemple int LongueurChaine2( char *ch ) { int i = 0; while ( ch[i] != ‘\0’) i++; return i; } int LongueurChaine1( char ch[ ] ) { int i = 0; while ( ch[i] != ‘\0’) i++; return i; } Ou encore int LongueurChaine3( char *ch ) { int i = 0; while ( *ch != ‘\0’) ch++; i++; } return i; char tab[3]; char * tabP=new char[3]; LongueurChaine1(tab); LongueurChaine2(tab); LongueurChaine1(tabP); LongueurChaine2(tabP); Bases de la programmation en C++
Pointeur et fonction Mettre un tableau en valeur de retour n’avait pas de sens car l’affectation n’était pas possible ensuite. Mettre un pointeur en retour de fonction est possible. char * CreateChaine (const int Size) { char * Chaine = new char [Size]; for (int i=0;i<Size-1;i++) Chaine [i] = 'A'; Chaine [Size-1] = '\0'; return Chaine; } int main() { char * tabP; tabP = CreateChaine(10); // OK // il faudra désallouer tabP //a la fin du programme char tab[10]; tab = CreateChaine(10); /// ERREUR } Bases de la programmation en C++