Sunteți pe pagina 1din 19

14/03/2007

Algorithmique et programmation

Chapitre 4 Les pointeurs avancs 4: (tableaux et fonctions)

Chap 4: Les pointeurs avancs


o o Comme nous l'avons dj constat au chapitre 3, le nom d'un tableau reprsente l'adresse de son premier lment. En d'autre termes: &tableau[0] et tableau sont une seule et mme adresse. le nom d'un tableau est un pointeur constant sur le premier lment du tableau. Exemple En dclarant 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];

14/03/2007

Chap 4: Les pointeurs avancs

o o

Si P pointe sur une composante quelconque d'un tableau, alors P+1 pointe sur la composante suivante. Plus gnralement, P+i pointe sur la i-ime composante derrire P et P-i pointe sur la i-ime composante devant P.

Chap 4: Les pointeurs avancs


o Ainsi, aprs l'instruction, P = A; o le pointeur P pointe sur A[0], et *(P+1) dsigne le contenu de A[1] *(P+2) dsigne le contenu de A[2] ... ... *(P+i) dsigne le contenu de A[i]

14/03/2007

Chap 4: Les pointeurs avancs


o o Exemple Soit A un tableau contenant des lments du type float et P un pointeur sur float:
float A[20], X; float *P;

Aprs les instructions,


P = A; X = *(P+9);

X contient la valeur du 10-ime lment de A, (c.--d. celle de A[9]). Une donne du type float ayant besoin de 4 octets, le compilateur obtient l'adresse P+9 en ajoutant 9 * 4 = 36 octets l'adresse dans P.

Chap 4: Les pointeurs avancs


o o Rassemblons les constatations ci dessus : Comme A reprsente l'adresse de A[0], *(A+1) dsigne le contenu de A[1] *(A+2) dsigne le contenu de A[2] *(A+i) dsigne le contenu de A[i]

14/03/2007

Chap 4: Les pointeurs avancs


o Attention !

Il existe toujours une diffrence essentielle entre un pointeur et le nom d'un tableau:
n Un pointeur est une variable, donc des oprations comme P = A ou P++ sont permises. Le nom d'un tableau est une constante, donc des oprations comme A = P ou A++ sont impossibles.

Chap 4: Les pointeurs avancs


o
o

Soit un tableau A d'un type quelconque et i un indice pour les composantes de A, alors
A dsigne l'adresse de A[0] A+i dsigne l'adresse de A[i] *(A+i) dsigne le contenu de A[i]

Si P = A, alors
P pointe sur l'lment A[0] P+i pointe sur l'lment A[i] *(P+i) dsigne le contenu de A[i]

Formalisme tableau et formalisme pointeur


Il nous est facile de 'traduire' un programme crit l'aide du 'formalisme tableau' dans un programme employant le 'formalisme pointeur'.

14/03/2007

Chap 4: Les pointeurs avancs


o Exemple
main() { int T[5] = {-3, 4, 0, -7, 3}; int POS[5]; int I,J; /* indices courants dans T et POS */ for (J=0,I=0 ; I<5 ; I++) if (T[I]>0) { POS[J] = T[I]; J++; } return 0; } main() { int T[5] = {-3, 4, 0, -7, 3}; int POS[10]; int I,J; /* indices courants dans T et POS */ for (J=0,I=0 ; I<5 ; I++) if (*(T+I)>0) { *(POS+J) = *(T+I); J++; } return 0; }

Chap 4: Les pointeurs avancs


o
o

Sources d'erreurs
Un bon nombre d'erreurs provient de la confusion entre soit contenu et adresse, soit pointeur et variable. Revoyons donc les trois types de dclarations que nous connaissons jusqu'ici et rsumons les possibilits d'accs aux donnes qui se prsentent. Les variables et leur utilisation int A; dclare une variable simple du type int A dsigne le contenu de A &A dsigne l'adresse de A int B[]; dclare un tableau d'lments du type int B dsigne l'adresse de la premire composante de B. (Cette adresse est toujours constante) B[i] dsigne le contenu de la composante i du tableau &B[i] dsigne l'adresse de la composante i du tableau en utilisant le formalisme pointeur: B+i dsigne l'adresse de la composante i du tableau *(B+i) dsigne le contenu de la composante i du tableau

14/03/2007

Chap 4: Les pointeurs avancs


o o Suite du rappel int *P; dclare un pointeur sur des lments du type int. P peut pointer sur des variables simples du type int ou sur les composantes d'un tableau du type int. P dsigne l'adresse contenue dans P (Cette adresse est variable) *P dsigne le contenu de l'adresse dans P Si P pointe dans un tableau, alors P dsigne l'adresse de la premire composante P+i dsigne l'adresse de la i-ime composante derrire P *(P+i) dsigne le contenu de la i-ime composante derrire P

Chap 4: Les pointeurs avancs


o Exercice 4.1
Soit P un pointeur qui 'pointe' sur un tableau A: int A[] = {12, 23, 34, 45, 56, 67, 78, 89, 90}; int *P; P = A; Quelles valeurs ou adresses fournissent ces expressions: a) *P+2 b) *(P+2) c) &P+1 d) &A[4]-3 e) A+3 f) &A[7]-P g) P+(*P-10) h) *(P+*(P+8)-A[7])

o o o o o o o o

14/03/2007

Chap 4: Les pointeurs avancs


o Exercice 4.2
o Ecrire un programme qui lit deux tableaux (vecteurs) A et B et leurs dimensions NA et NB au clavier et qui ajoute les lments de B la fin de A. Utiliser le formalisme pointeur chaque fois que cela est possible. (Prvoir suffisamment despace en A pour stocker B)

Chap 4: Les pointeurs avancs


o Ecrire un programme qui lit un caractre C et une chane de caractres CH au clavier. Ensuite toutes les occurrences de C dans CH seront limines. Le reste des caractres dans CH sera tass l'aide d'un pointeur

14/03/2007

Chap 4: Les pointeurs avancs


o Allocation dynamique
Nous avons vu que l'utilisation de pointeurs nous permet de mmoriser conomiquement des donnes de diffrentes grandeurs. Si nous gnrons ces donnes pendant l'excution du programme, il nous faut des moyens pour rserver et librer de la mmoire au fur et mesure que nous en avons besoin. Nous parlons alors de l'allocation dynamique de la mmoire.

Chap 4: Les pointeurs avancs


o Chaque variable dans un programme a besoin d'un certain nombre d'octets en mmoire. Jusqu'ici, la rservation de la mmoire s'est droule automatiquement par l'emploi des dclarations des donnes. Dans tous ces cas, le nombre d'octets rserver tait dj connu pendant la compilation. Nous parlons alors de la dclaration statique des variables. Exemples
n n n n float A, B, C; /* rservation de 12 octets */ short D[10][20]; /* rservation de 400 octets */ char E[] = {"Bonjour !"}; /* rservation de 10 octets */ char F[][10] = {"un", "deux", "trois", "quatre"}; /* rservation de 40 octets */

14/03/2007

Chap 4: Les pointeurs avancs


o Pointeurs Le nombre d'octets rserver pour un pointeur dpend de la machine et du 'modle' de mmoire choisi, mais il est dj connu lors de la compilation. Un pointeur est donc aussi dclar statiquement. Supposons dans la suite qu'un pointeur ait besoin de p octets en mmoire. Exemples
n n n double *G; /* rservation de p octets */ char *H; /* rservation de p octets */ float *I[10]; /* rservation de 10*p octets */ Chanes de caractres constantes

Chap 4: Les pointeurs avancs


o Pointeurs L'espace pour les chanes de caractres constantes qui sont affectes des pointeurs ou utilises pour initialiser des pointeurs sur char est aussi rserv automatiquement: Exemples n char *J = "Bonjour !"; /* rservation de p+10 octets */ n float *K[] = {"un", "deux", "trois", "quatre"}; /* rservation de 4*p+3+5+6+7 octets */

14/03/2007

Chap 4: Les pointeurs avancs


o Allocation dynamique o Problme
Souvent, nous devons travailler avec des donnes dont nous ne pouvons pas prvoir le nombre et la grandeur lors de la programmation. Ce serait alors un gaspillage de rserver toujours l'espace maximal prvisible. Il nous faut donc un moyen de grer la mmoire lors de l'excution du programme.

Chap 4: Les pointeurs avancs


o Exemple
Nous voulons lire 10 phrases au clavier et mmoriser les phrases en utilisant un tableau de pointeurs sur char. Nous dclarons ce tableau de pointeurs par: char *TEXTE[10]; Pour les 10 pointeurs, nous avons besoin de 10*p octets. Ce nombre est connu ds le dpart et les octets sont rservs automatiquement. Il nous est cependant impossible de prvoir l'avance le nombre d'octets rserver pour les phrases elles-mmes qui seront introduites lors de l'excution du programme ...

o o

Allocation dynamique La rservation de la mmoire pour les 10 phrases peut donc seulement se faire pendant l'excution du programme programme. Nous parlons dans ce cas de l'allocation dynamique de la mmoire.

10

14/03/2007

Chap 4: Les pointeurs avancs


o La fonction malloc
o La fonction malloc de la bibliothque <stdlib> nous aide localiser et rserver de la mmoire au cours d'un programme. Elle nous donne accs au tas (heap); c.--d. l'espace en mmoire laiss libre une fois mis en place. La fonction malloc malloc( <N> ) fournit l'adresse d'un bloc en mmoire de <N> octets libres ou la valeur zro s'il n'y a pas assez de mmoire. Attention ! Sur notre systme, le paramtre <N> est du type unsigned int. A l'aide de malloc, nous ne pouvons donc pas rserver plus de 65535 octets la fois!

Chap 4: Les pointeurs avancs


o Exemple Supposons que nous ayons besoin d'un bloc en mmoire pour un texte de 4000 caractres. Nous disposons d'un pointeur T sur char (char *T). Alors l'instruction: T = malloc(4000); fournit l'adresse d'un bloc de 4000 octets libres et l'affecte T. S'il n'y a plus assez de mmoire, T obtient la valeur zro.

11

14/03/2007

Chap 4: Les pointeurs avancs


o Si nous voulons rserver de la mmoire pour des donnes d'un type dont la grandeur varie d'une machine l'autre, nous avons besoin de la grandeur effective d'une donne de ce type. L'oprateur sizeof nous aide alors prserver la portabilit du programme. L'oprateur unaire sizeof
n n n sizeof <var> fournit la grandeur de la variable <var> sizeof <const> fournit la grandeur de la constante <const> sizeof (<type>) fournit la grandeur pour un objet du type <type>

Chap 4: Les pointeurs avancs


o Exemple Nous voulons rserver de la mmoire pour X valeurs du type int; la valeur de X est lue au clavier:
int X; int *PNum; printf("Introduire le nombre de valeurs :"); scanf("%d", &X); PNum = malloc(X*sizeof(int));

La commande exit S'il n'y a pas assez de mmoire pour effectuer une action avec succs, il est conseill d'interrompre l'excution du programme l'aide de la commande exit (de <stdlib>) et de renvoyer une valeur diffrente de zro comme code d'erreur du programme

12

14/03/2007

Chap 4: Les pointeurs avancs


o Si nous n'avons plus besoin d'un bloc de mmoire que nous avons rserv l'aide de malloc, alors nous pouvons le librer l'aide de la fonction free de la bibliothque <stdlib>. free( <Pointeur> ) libre le bloc de mmoire dsign par le <Pointeur>; n'a pas d'effet si le pointeur a la valeur zro. Attention !
n n La fonction free peut aboutir un dsastre si on essaie de librer de la mmoire qui n'a pas t alloue par malloc. La fonction free ne change pas le contenu du pointeur; il est conseill d'affecter la valeur zro au pointeur immdiatement aprs avoir libr le bloc de mmoire qui y tait attach. Si nous ne librons pas explicitement la mmoire l'aide free, alors elle est libre automatiquement la fin du programme.

Chap 4: Les pointeurs avancs


o Ecrire un programme qui lit 10 phrases d'une longueur maximale de 200 caractres au clavier et qui les mmorise dans un tableau de pointeurs sur char en rservant dynamiquement l'emplacement en mmoire pour les chanes. Ensuite, l'ordre des phrases est invers en modifiant les pointeurs et le tabl

for (I=0,J=9 ; I<J ; I++,J--) { PAIDE = TEXTE[I]; TEXTE[I] = TEXTE[J]; TEXTE[J] = PAIDE; }

13

14/03/2007

Chap 4: Les pointeurs avancs


o Exercice N4.3
o Ecrire un programme qui lit 10 mots au clavier (longueur maximale: 50 caractres) et attribue leurs adresses un tableau de pointeurs MOT. Copier les mots selon l'ordre lexicographique en une seule 'phrase' dont l'adresse est affecte un pointeur PHRASE. Rserver l'espace ncessaire la PHRASE avant de copier les mots. Librer la mmoire occupe par chaque mot aprs l'avoir copi. Utiliser les fonctions de <string>.

Chap 4: Les pointeurs avancs


#include <stdio.h> #include <stdlib.h> #include <string.h> main() { char INTRO[500]; char *TEXTE[10]; int I; for (I=0; I<10; I++) { gets(INTRO); TEXTE[I] = malloc(strlen(INTRO)+1); if (TEXTE[I]) strcpy(TEXTE[I], INTRO); else { printf("ERREUR: Pas assez de mmoire \n"); exit(-1); } } return 0; }

Exemple

14

14/03/2007

Chap 4: Les pointeurs avancs


o Les pointeurs et les tableaux 2 dimensions
L'arithmtique des pointeurs se laisse largir avec toutes ses consquences sur les tableaux deux dimensions. Voyons cela sur un exemple: Exemple Le tableau M deux dimensions est dfini comme suit: int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {10,11,12,13,14,15,16,17,18,19}, {20,21,22,23,24,25,26,27,28,29}, {30,31,32,33,34,35,36,37,38,39}};

Chap 4: Les pointeurs avancs


o Le nom du tableau M reprsente l'adresse du premier lment du tableau et pointe (oh, surprise...) sur le tableau M[0] qui a la valeur: {0,1,2,3,4,5,6,7,8,9}. o L'expression (M+1) est l'adresse du deuxime lment du tableau et pointe sur M[1] qui a la valeur: {10,11,12,13,14,15,16,17,18,19} .

15

14/03/2007

Chap 4: Les pointeurs avancs


o Explication
o Au sens strict du terme, un tableau deux dimensions est un tableau unidimensionnel dont chaque composante est un tableau unidimensionnel. Ainsi, le premier lment de la matrice M est le vecteur {0,1,2,3,4,5,6,7,8,9}, le deuxime lment est {10,11,12,13,14,15,16,17,18,19} et ainsi de suite. L'arithmtique des pointeurs qui respecte automatiquement les dimensions des lments conclut logiquement que: M+I dsigne l'adresse du tableau M[I]

Chap 4: Les pointeurs avancs


o Problme
Comment pouvons-nous accder l'aide de pointeurs aux lments de chaque composante du tableau, c.-d.: aux lments M[0][0], M[0][1], ... , M[3][9] ? Discussion Une solution consiste convertir la valeur de M (qui est un pointeur sur un tableau du type int) en un pointeur sur int. On pourrait se contenter de procder ainsi: int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {10,11,12,13,14,15,16,17,18,19}, {20,21,22,23,24,25,26,27,28,29}, {30,31,32,33,34,35,36,37,38,39}}; int *P; P = M; /* conversion automatique */

16

14/03/2007

Chap 4: Les pointeurs avancs


o Cette dernire affectation entrane une conversion automatique de l'adresse &M[0] dans l'adresse &M[0][0]. (Remarquez bien que l'adresse transmise reste la mme, seule la nature du pointeur a chang). Cette solution n'est pas satisfaisante 100%: Gnralement, on gagne en lisibilit en explicitant la conversion mise en uvre par l'oprateur de conversion force ("cast"), qui vite en plus des messages d'avertissement de la part du compilateur

Chap 4: Les pointeurs avancs


o Solution propre int *P; P = (int *)M; /* conversion force */ D la mmorisation ligne par ligne des tableaux deux dimensions, il nous est maintenant possible de traiter M l'aide du pointeur P comme un tableau unidimensionnel de dimension 4*10.

17

14/03/2007

Chap 4: Les pointeurs avancs


o Exemple
o Les instructions suivantes calculent la somme de tous les lments du tableau M:
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {10,11,12,13,14,15,16,17,18,19}, {20,21,22,23,24,25,26,27,28,29}, {30,31,32,33,34,35,36,37,38,39}}; int *P; int I, SOM; P = (int*)M; SOM = 0; for (I=0; I<40; I++) SOM += *(P+I);

Chap 4: Les pointeurs avancs


o Si vous avez compris
o int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {10,11,12,13,14,15,16,17,18,19}, {20,21,22,23,24,25,26,27,28,29}, {30,31,32,33,34,35,36,37,38,39}}; A quoi correspond *(*(M+2)+4) ? =M[2][4]

18

14/03/2007

Chap 4: Les pointeurs avancs


o Et pour finir
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i,j; int val1=10,val2=5; int traiter(int (*pf)(int a1,int b1),int a, int b); int fonc1(int a, int b); int fonc2(int a, int b); i=traiter traiter(fonc1,val1,val2); j=traiter traiter(fonc2,val1,val2); printf("valeur i:%d \t valeur j:%d\n",i,j); system("PAUSE"); return 0; } int fonc1(int a1, int b1) { printf("Passage dans fonc1\n"); return(a1+b1); } int fonc2(int a1, int b1) { printf("Passage dans fonc2\n"); return(a1-b1); } int traiter int (*pf)(int a1,int b1),int a, int b) traiter( { int dummy; dummy=2*(*pf)(a,b); printf("dummy %d \n",dummy); return dummy; }

19

S-ar putea să vă placă și