Vous êtes sur la page 1sur 33

1

4. Pointeurs et allocation dynamique


•  Définitions et déclarations
•  Tableaux et pointeurs
•  Structures
•  Tableaux multidimensionnels
2

Pointeurs
•  Chaque variable a une adresse en mémoire qu’il peut être utile de connaître.
•  Pour obtenir cette adresse : opérateur “adresse de“ &
p = &i ; affecte adresse de i dans p
•  Exemple :
void main ()
{ int i = 7 ;
printf(“ valeur de i = %d\n “, i);
printf(“ adresse de i = %p\n “, &i);
}
@ memoire:
•  Résutat :
> valeur de i = 7 0x03EC 24
> adresse de i = 0x03F0 (1008(10)) 0x03F0 7 i

0x03F4 542
3

Pointeurs
•  Un pointeur est une variable pouvant contenir une adresse
mémoire.

Donc p = &i ; affecte l’adresse de i à la variable p &i


On dit que p “pointe“ désormais sur i. p

•  * : opérateur d’indirection ou de déréférencement;


Appliqué à un pointeur, donne accès à l’objet pointé.

Donc j = *p ; j = objet pointé par p


&i 7
j = i;
p i
/* i et *p interchangeables*/ *
4

Pointeurs
§  Déclaration : en fonction du type de l’objet pointé
Entier : int *i; /* car *i est un entier */ &?
Réel: float *f ; i
Caractère: char *c;

q  Exemple issu du K&R:


int x = 1, y = 2, z[10]; &?
int *pi; /* pi est un pointeur sur 1 int */ pi
&x 1
pi = &x; /* pi pointe maintenant sur x */
pi x
y = *pi; /* y vaut désormais 1 */ *

*pi = 0; /* x vaut désormais 0 */ &x 0


pi * x
pi = &z[0]; /*pi pointe désormais sur z[0]*/ &z[0] ? ?
pi z[0] z[1]
5

Pointeurs
  Opérations sur les objets pointés : les opérations permises sur l’objet
int *pi = &x;
*pi = *pi + 2; /* *pi et x ici interchangeables */
/* <-> x = x + 2 */

y = *pi – 1; /* <-> y = x – 1 */

(*pi)++; /* idem. (*pi) += 1; <-> x++ */

  Opération sans indirection : car les pointeurs sont aussi des variables
int *qi, *pi; &f
&e
avant: qi pi
qi = pi ; /* qi pointe sur même objet que pi */

après: &f &f


qi pi
6

Pointeurs
•  Initialisation : int a,*pi; &? ?
pi a
(1) pi=&a; &a ?
pi a
*pi=3; //ok 3
&a
pi a
*
(2) avec malloc() (voir plus loin)
§  Ne pas utiliser un pointeur non initialisé par l’adresse effective d’une variable !!
§  Déclarer un pointeur réserve un espace mémoire pour stocker une adresse
mais en aucun cas pour l’objet pointé. &?
pi
int *pi;
*pi=3; /* pointeur non initialisé, contient une & aléatoire*/
/* et pas de mémoire réservée pour stocker l’entier &? pointé*/
pi
*
Attention: ce code ne fait pas d’erreur à la compilation
mais une erreur à l’exécution (Segmentation Fault)
7

Pointeurs

xkcd.com
8

Tableaux et pointeurs
•  Par définition: si pa est un pointeur sur un élément d’un tableau
alors,
•  pa+i pointe sur le i-ème élément du tableau après pa
•  pa-i pointe sur le i-ème élément avant pa

int *pa, a[5];


pa = &a[0]; /*pa point sur l’élément 0 de a*/
pa+1 pa+2 …
&a[0]
pa
? ? ? ? ?
a[0] a[1] a[2] a[3] a[4]

Note : si pa contient 0x10, l’adresse du premier élément, alors


pa+1 ou p++ vaut 0x14 dans le cas où l’entier signé est codé sur 4 octets.
9

Tableaux et pointeurs
Ainsi a[i] identique à *(pa+i), si pa pointe sur le premier élément:

&a[0] pa+1 pa+2 …


pa
? ? 12 ? ?
*(pa+2)=12; /* idem. a[2]=12*/
a[0] a[1] a[2] a[3] a[4]

*pa = *(pa+2);
&a[0] 12 ? 12 ? ?
/* noter l’importance
des parenthèses */ pa a[0] a[1] a[2] a[3] a[4]

*pa = *pa + 2 ; &a[0] 14 ? 12 ? ?


pa a[0] a[1] a[2] a[3] a[4]
10

Tableaux et pointeurs
•  Le nom d’un tableau vaut l’adresse de son 1er élément: a ≈ &a[0]

fondamental!
Donc, *(a+i) identique à a[i] a a+1 a+2 …

? ? ? ? ?
a[0] a[1] a[2] a[3] a[4]
int a[5];
pa+1
*a=7; /* affecte 7 à a[0] */
a a+1
*(a+1)=2; /* idem a[1] = 2 */
int *pa = a; /* idem. pa = &a[0]; */
a 7 2
pa a[0] a[1]
Note : pa est une variable donc pa = a; puis pa = b; et pa++; VALIDES,
mais a est juste un nom donc a = pa; et a++ ; INVALIDES.
11

Tableaux et pointeurs
aussi : a+i identique à &a[i]

*(a+2)=12; /* idem. a[2]=12*/ a a+1 a+2 …

? ? 12 ? ?
a[0] a[1] a[2] a[3] a[4]
*a = *(a+2);
/* noter l’importance a
des parenthèses sinon :*/
12 ? 12 ? ?
a[0] a[1] a[2] a[3] a[4]
*a = *a + 2 ; a

14 ? 12 ? ?
a[0] a[1] a[2] a[3] a[4]
12

Allocation dynamique de la mémoire


¨  Allocation statique: Taille d'un tableau doit être une constante.
•  int tab[50];
•  int tab[]={3,4,5,6} ( ≈ tab[4] )
•  int tab[n] interdit ! (bien qu’une norme et des compilateurs
récents l’autorisent maintenant L)

•  Or la taille n’est parfois pas connue à l’avance.


Ø Surdimensionner le tableau (ex: tab[4000])
mais probablement un gâchis de mémoire inutilisable

Ø Définir la taille pendant l’exécution (Allocation dynamique) :


1.  une fonction malloc() recherche un bloc mémoire libre contigüe de
taille désirée et renvoie son adresse.
2.  cette adresse est affectée à un pointeur pour utilisation du bloc mémoire.
13

Allocation dynamique de la mémoire


¨  Allocation dynamique: malloc()

§  malloc (int size) : recherche un bloc contigüe de size octets


libre en mémoire et retourne l’adresse de début du bloc.
Retourne le pointeur NULL, si échec (pas assez de mémoire libre).

int n=4, *tb; &?


tb

tb = (int*) malloc ( n*sizeof(int) );


&bloc ? ? ? ?

tb+1 …
Accès (similaire au tableau statique) :
tb[i] ou *(tb+i) &bloc ? ? ? ?
tb tb[0] tb[1] …
14

Allocation dynamique de la mémoire


¨  Allocation dynamique:

§  NULL est un symbole constant valant 0, défini dans stdlib.h.

§  sizeof(type): renvoie la taille en octets de l’objet de type type

§  malloc() renvoie un pointeur de type void*.

§  void *ptr; définit un pointeur générique, i.e dont le type de l’objet
pointé est indéfini. Avant utilisation, le convertir explicitement:
int *pi;
pi=(int*) ptr; /* pi contient la même adresse que ptr */
/* mais pi pointe sur un entier de taille sizeof(int)
et pi+1 sur l’entier suivant */
15

Allocation dynamique de la mémoire


#include <stdio.h>
#include <stdlib.h> /* pour le prototype de malloc() */

int main(void)
{ int n, *tab;
printf (“Taille désirée?“);
scanf (“%d“,&n);
tab=(int*) malloc (n*sizeof(int)); /* allocation dyn. de n entiers */

if (tab==NULL) /* test si échec de l’allocation */


{ printf(“erreur allocation tab“);
return(-1);} /*sort du programme si échec!*/

tab[0]=1; /* idem. *tab=1; */


free(tab); /* libération du bloc mémoire alloué par malloc*/
return(0); }
16

Allocation dynamique de la mémoire


  Libération de la mémoire allouée par malloc() :
§  free(void *ptr): libère le bloc mémoire d’adresse ptr
précédemment alloué par un malloc.

§  Toujours libérée la mémoire dès que l’on en a plus besoin (en fin de
programme ?)

§  La mémoire libérée en cours d’exécution peut être réutilisée pour


d’autres allocations.

•  Note: malloc et free peuvent être utilisés pour allouer un bloc mémoire
une variable du type pointé (un tableau d’1 seul élément).
17

Erreur de Segmentation
  Segmentation de la mémoire :
§  Le système d’exploitation alloue de la mémoire à chaque programme.
§  Un programme ne peut accéder (lecture/écriture) à la mémoire d’un autre
programme par sécurité: un programme avec des erreurs ne peut ainsi
corrompre un autre programme.

  Erreur de Segmentation (Segmentation Fault):


§  Si un programme tente d’accéder de la mémoire qui ne lui appartient pas

Ø  détection par le système de la violation mémoire qui envoie un signal


(SIGSEGV) au programme
Ø  avec pour résultat de stopper immédiatement l’exécution du programme
18

Erreur de Segmentation
  Cause courante d’erreur de segmentation :
§  Avec scanf (““, void *) dont les arguments sont des adresses
où écrire les donnés lues au clavier,

int n; /*n non initialisé*/


scanf (“%d“, n); /*adresse de stockage:
contenu aléatoire de n */

/* correct : scanf (“%d“,&n) -> adresse : adresse de n*/

§  Utiliser un pointeur non initialisé (déjà vu)


int *pi; /* pi contient une adresse aléatoire */
*pi = 3; /* accès en écriture à une adresse
aléatoire: risque de Seg Fault*/
&? ?
pi ?
*
19

Erreur de Segmentation
  Cause commune d’erreur de segmentation :
§  Débordement de tableau statique ou dynamique

int a[2]; a-1 a a+1 a+2


a[2] = 3; /* idem *(a+2)=3 */
*(a - 1) = 1; ? ? ? ?
a[0] a[1]

Note: parfois pire (pas de segmentation fault) mais un programme bogué


int N=2, a[2];
a[2] = -1; /* hors tableau et tombe sur N*/
N++; /* N vaut 0 !!*/ a a+1 a+2

si le hasard fait que l’occupation mémoire est: ? ? ?


a[0] a[1] N
20

Les Structures
  regroupe des éléments de types différents
  facilite l’organisation et la manipulation de données composées
  un composant est appelé un champ ou un membre

 déclaration de structure: struct nom_facultatif_pour_la_struct


{ définition };
struct coureur {
char nom[30] ; idem:
char prenom[30] ; struct coureur {
int dossard ;
char nom[30] ;
float temps ; char prenom[30];
}; int dossard ;
float temps ;
struct coureur c1, c2, c3 ;
/* c1, c2, c3 sont des structures }c1, c2, c3;
de type coureur */
21

Les Structures
  Initialisation à la déclaration :
struct coureur c1={“Bolt“,“Usain“,23,9.58};

  .“
accès aux champs par l’opérateur “
c1.temps = 9.54 ; c2.dossard = 2 ;

  structures et pointeurs
struct coureur *c4 ; /*n’alloue pas de mémoire
pour la structure*/
c4 = (struct coureur *)malloc(sizeof(struct coureur));

(*c4).temps = 42.54 ; /* parenthèse nécessaire */

c4->temps = 42.54 ; /* idem avec opérateur “->“


pour alléger l’écriture */
22

typedef
  permet de définir des noms de nouveau type de données
  Exemple : typdef int Longueur;
/*rend Longueur synonyme de int*/
Longueur i, *j; /* définit 1 variable et 1 pointeur (sur entier)*/

  allège les déclarations de structure


typedef struct { char nom[30] ;
char prenom[30] ;
int dossard ;
float temps ;
} coureur ;

/* désormais on peut omettre le mot-clé struct :*/

coureur c1 , c2 , *c3 ;

c3 = (coureur*) malloc (1*sizeof(coureur));


23

Segmentation Fault
24

Tableaux multidimensionnels
•  Un tableau à 2 dimensions (L,C) est un tableau de tableau :
un tableau L éléments (/lignes) contenant chacun un tableau de C éléments.

•  Déclaration : int a[L][C]; L lignes de C éléments


int a[2][3]={{1,2,3},{4,5,6}}; avec initialisation

a[0] a[1]

a: 1 2 3 4 5 6
a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]

a[0][0] a[0][1] a[0][2]


•  on se permet de le représenter a[0] 1 2 3
ainsi pour plus de lisibilité : a[1] 4 5 6
a[1][0] a[1][1] a[1][2]
25

Tableaux multidimensionnels
•  Comme pour un tableau 1D: a ≈ &a[0][0]
•  ( a[0]est le nom du tableau de la première ligne , a[0] ≈ &a[0][0]
et a[1] ≈ &a[1][0] )
a a[0][0] a[0][1] a[0][2]
a[0] 1 2 3
a[1] 4 5 6
a[1][0] a[1][1] a[1][2]
•  Accès aux composantes :
a[1]+1
a[i][j] (à préférer J)

ou *( a[i] + j )
ou encore *( *(a+i) + j )
26

Tableaux multidimensionnels
#include <stdio.h>

void main(void)
{ int m[2][3] = {{11,12,13},{21,22,23}};

*((*m)+1) = -2;
m[1][0]= -1;

printf("%i %i %i \n",
m[0][0], m[0][1], m[0][2]);
printf("%i %i %i \n",
**(m+1), *(*(m+1)+1), *( m[1]+2));
}

Sortie écran:
11 -2 13
-1 22 23
27

Le tableau de pointeurs !

s[2]
s[0]

s[1]
s O n \0
char s[] = "On"; /* idem. char s[3]= .. */
char s1[] = "est"; s1 e s t \0
char s2[] = "jeudi";
s2 j e u d i \0

Si on souhaite mettre les chaines dans un tableau :


un tableau avec les adresses du 1er élément des chaînes (le noms des chaînes)
≈ un tableau de pointeurs vers des caractères

s[2]
s[0]

s[1]
char * tabs[] = {s, s1, s2}; tabs
/* idem ….= { &s[0], &s1[0], &s2[0] } */ s O n \0
tabs[1][2] = x; s1 e s x \0
/* car idem *( *(tabs+1) + 2)=… */ s2 j e u d i \0
28

Le tableau de pointeurs !
Accès au caractères (toujours pareil):
tabs[i][j] ou *( *(tabs+i)+j )

car tabs[i] ou *(tabs+i) est l’adresse du 1er élément de la chaine.

On peut définir un pointeur sur le tableau de pointeurs


(è pointeur sur un pointeur sur 1 caractère):

char ** ptr_s = tabs; tabs

s[2]
s[0]

s[1]
L’accès (toujours pareil) : tabs s O n \0
ptr_s[1][2] = X; ptr_s s1 e s X \0
ou s2 j e u d i \0
*(*(ptr_s+1)+2) = X;
29

Tableau de pointeurs sur entiers !


mat

a[2]
a[0]

a[1]
int a[3], b[4], c[3];
int * mat[3] = {a,b,c};
( tab ) a 1 -4 0
ptr_m b 7 2 11 1
( int ** ptr_m = tab; ) c -4 6 9

Le tableau int* mat[3] peut être alloué dynamiquement puis remplit avec les noms
(adresses) des tableaux:
&bloc a 1 -4 0
ptr_m b 7 2 11 1
int **ptr_m;
c -4 6 9
ptr_m = (int**) malloc ( 3*sizeof(int*) );
ptr_m[0] = a;

On peut encore allouer les tableaux a, b et c dynamiquement: tableau 2D dynamique.
30

Tableaux multidimensionnels dynamiques


•  Allocation dynamique : d’un tableau de dimension (3,4)

1.  Allocation d’un tableau de 3 pointeurs sur des entiers


&bloc &?
int ** ptr_m; ptr_m &?
ptr_m = (int**) malloc(n*sizeof(int *));
&?

2.  On alloue 3 tableaux de 4 entiers dont on stocke les adresses


dans le tableau de pointeurs avec malloc() &bloc’ ? ? ? ?

ptr_m[0][0]

&bloc &bloc’ ? ? ? ?
ptr_m &bloc’’ ? ? ? ?
&bloc’’’ ? ? ? ?
31

Tableaux multidimensionnels
void main(void)
{ int i; int Lignes = 3,Colonnes = 4;
int ** ptr_m; /* pointeur sur un pointeur */

ptr_m = (int**) malloc(sizeof (int*) * Lignes);


/* allocation du tableau de pointeurs */
for (i = 0; i < Lignes; i++)
ptr_m[i] = (int*) malloc(sizeof (int) * Colonnes);
/* allocation des tableaux d’entiers (chq ligne)*/
ptr_m[1][3] = 77;

for (i = 0; i < Lignes; i++)


free (ptr_m[i]); /* libération du tableau de chaque ligne */
/* ou avec free(mat[i]); */
free(ptr_m); /* libération du int** */
}
32

Tableaux multidimensionnels
•  Autre solution: considérer le tableau 2D comme un vecteur

1 2 3 4

1ère ligne 2ème ligne

•  Déclaration : int matrice [L*C] ;


ou int *matrice=(int*)malloc(L*C*sizeof(int));

•  Accès à l’élément correspondant à “ matrice[i][j]“:

matrice[i*C+j] ou *(matrice + (i*C) + j)


33

Tableaux multidimensionnels
#include <stdio.h>
#include <stdlib.h>

void main(void)
{
int m_toto[6] = {11,12,13,21,22,23}; //matrice 2x3

printf("%i %i %i \n",
m_toto[0*3+0], m_toto[0*3+1], m_toto[0*3+2]);
printf("%i %i %i \n",
*(m_toto+1*3), *(m_toto+1*3+1), *(m_toto+1*3+2));
}

Sortie écran:
11 12 13
21 22 23

Vous aimerez peut-être aussi