0% ont trouvé ce document utile (0 vote)
67 vues33 pages

Pointeurs Et Allocation Dynamique

Transféré par

Fadhul Djirame
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 PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
67 vues33 pages

Pointeurs Et Allocation Dynamique

Transféré par

Fadhul Djirame
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 PDF, TXT ou lisez en ligne sur Scribd

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

[Link]
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 “
[Link] = 9.54 ; [Link] = 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