CINI – Li115 1 Semaine 11 Les pointeurs (suite) ● Tableaux et pointeurs ● Questions sur les pointeurs
CINI – Li115 2 Tableaux et pointeurs (1) ● Tableau = pointeur ● int tab[3];→ ● Allocation de 3 cases dans la mémoire ● tab pointeur vers la première case ● tab &(tab[0]) ● NB : en réalité, la variable « tab » n'est pas matérialisée, sauf si c'est un paramètre de fonction ● tab[i] = la i ème valeur après celle pointée par tab – Donc tab[0] *tab
CINI – Li115 3 Tableaux et pointeurs (2) ● Tableau paramètre de fonction ● void f(int tab[]) { … } – → c'est le pointeur qui est passé en paramètre, c'est-à- dire l'adresse de la première case du tableau ● void f(int *tab) { … } ● → D'où le tableau modifiable par la fonction : quand on passe un tableau en paramètre, on passe en fait l'adresse de la première case. On peut ensuite modifier ce qui se trouve à cette adresse c'est-à-dire le contenu du tableau – => Passage par référence ● → cela explique pourquoi les modifications faites sur un tableau dans une fonction sont répercutées en dehors de la fonction.
CINI – Li115 4 Tableaux et pointeurs (3) ● Tableau paramètre de fonction void ajouteTab(int tab[], int taille, int val){ int i; for(i=0; i < taille; i++){ tab[i] = tab[i] + val; } void ajouteTab(int *tab, int taille, int val){ int i; for(i=0; i < taille; i++){ tab[i] = tab[i] + val; }
CINI – Li115 5 Tableaux et pointeurs (4) ● Tableau résultat de fonction ? ● int f()[] { … } – → erreur: ‘f’ declared as function returning an array ● → Interdiction de retourner un tableau ● En revanche une fonction peut retourner un pointeur : ● Mais l'adresse retournée ne doit pas correspondre à l'adresse d'une variable locale (car sinon la mémoire sera libérée en sortant de la fonction et l'adresse ne correspondra plus à rien). ● Une autre méthode : procéder à l'allocation dynamique de la mémoire en utilisant malloc et calloc (ne sera pas abordée en LI115 mais en LI215)
CINI – Li115 6 Tableaux et pointeurs (5) ● Tableau = pointeur ● → pointeur comme résultat de fonction ? – NON OUI ● int *f() {int *f(int *p) { ● int tab[3]; return (p+1); ● return tab;} ● } – → attention : cette fonction retourne l'adresse d'une variable locale – → Risque très élevé d'erreur lors de l'exécution ● Au choix: ● – Erreur de segmentation = le programme cherche à accéder à une partie de la mémoire qui ne lui est pas (plus) allouée ● – Erreur d'exécution : les données pointées dans le tableau ont été modifiées car la zone mémoire a été utilisée pour une autre portion du programme.
CINI – Li115 7 Tableaux et pointeurs (6) ● Tableau et pointeur : arithmétique des pointeurs ● tab[i] *(tab+i) ● Le compilateur connaît le type des éléments du tableau ● → il fait des sauts de N octets en N octets (selon le type des éléments du tableau) void init(int t[], int n) { int i; for(i=0;i<n;i++) t[i] = 3*i; } int main() { int tab[3]; init(tab,3); return 0; } void init(int *t, int n) { int i; for(i=0;i<n;i++) *(t+i) = 3*i; } int main() { int tab[3]; init(tab,3); return 0; }
CINI – Li115 8 Tableaux et pointeurs (7) ● Recopie de tableaux ? ● int t1[2],t2[2]; ● t1 = t2;→ Interdit (erreur : incompatible types...) ● Mais … ● int t1[2],*t2; ● t2 = t1;→ OK ● Attention : – Vous n'avez pas recopié le tableau … seulement le pointeur ! → Les deux « tableaux » pointent sur les mêmes cases (ils contiennent la même adresse). – t1[1] = 3; t2[1] = 0; CINI_print_int(t1[1]);→ Affiche 0
CINI – Li115 9 Déclaration et initialisation de pointeurs ● Soit : ● int a =12, b ; char c; float ff = 12.3; ● Quelles sont les déclarations correctes ? Corrigez. ● int * aa = a ; ● int * ab = &a; ● int * ac = *ab; ● char * ca = &ff; ● char * cb = &c; ● float* fa = ab; ● float fb = &ff; ● &fa = 29.7; correction OK warning: initialization makes pointer from integer without a cast int * aa = &a; warning: initialization makes pointer from integer without a cast i nt *ac = ab; warning: initialization from incompatible pointer type OK warning: initialization from incompatible pointer type error: incompatible types in initialization error: invalid type argument of `unary *' i nt *ac = ab; float *fa = &ff; f loat fb = ff; ou float fb = *fa; *fa = 29.7;
CINI – Li Fonctions et pointeurs ● Soit la fonction main suivante : ● int main(){ ● int num = 739, unit, diz, cent; ● /* appel de la fonction decompose */ ● CINI_print_string("decomposition de "); ● CINI_print_int(num); ● CINI_print_string("centaine : "); ● CINI_print_int(cent); ● CINI_print_string("dizaine : "); ● CINI_print_int(diz); ● CINI_print_string("unite : "); ● CINI_print_int(unit); ● return 0; ● } ● Définissez la fonction decompose et écrivez l'instruction d'appel de cette fonction. correction
CINI – Li Fonctions et pointeurs void decompose(int num, int* c, int * d, int * u){ *u = num % 10; *d = (num % 100) /10; *c = (num % 1000) / 100; } ● int main(){ ● int num = 739, unit, diz, cent; decompose (num, ¢, &diz, &unit); ● CINI_print_string("decomposition de "); ● CINI_print_int(num); ● CINI_print_string("centaine : "); ● CINI_print_int(cent); ● CINI_print_string("dizaine : "); ● CINI_print_int(diz); ● CINI_print_string("unite : "); ● CINI_print_int(unit); ● return 0; } correction
CINI – Li Fonctions et pointeurs Soit g une fonction dont la signature est la suivante : float g(float *x, float y); L’appel suivant de la fonction g est il correct ? Justifiez par rapport à tous les types en cause. float t[] = {1,2}; float d = g(t,1.0); Même question avec l'appel suivant : int u[] = {1,2}; u[1] = g(u+1,u[0]); correction Oui, car 1) le premier argument est le tableau t qui est aussi un pointeur (vers le premier élément du tableau), donc il est bien de type float* ; 2) le deuxième argument est bien une expression de type float ; 3) la fonction g retourne bien un double. Non, car le premier paramètre passé à g(u+1) est est de type int* alors qu’il faut une expression de type float*.
CINI – Li Tableaux et fonctions ● Qu'affiche le code suivant ? Pourquoi ? #include void f2(int tab[], int taille){ int i; for (i=0;i<taille;i++) if (tab[i] > 3) tab[i] = tab[i]/2; } int main(){ int tabf[5] = {1,2,6,3,7}; int i; for (i=0;i<5;i++) CINI_print_int(tabf[i]); CINI_newline(); f2(tabf,5); for (i=0;i<5;i++) CINI_print_int(tabf[i]); CINI_newline(); return 0; } Un tableau en paramètre d’une fonction est passé par référence et non par valeur. correction
CINI – Li Pointeurs et passage par référence Écrivez une fonction compte qui ne retourne rien et qui prend en paramètres un tableau de caractères tab, sa taille taille et un caractère c. La fonction doit permettre de récupérer le nombre d'apparitions du caractère dans le tableau. correction Comme la fonction ne retourne aucune valeur, elle doit rendre son résultat grâce à un paramètre passé par référence. prototype de compte : void compte(char tab[], int taille, char c, int *nb);
CINI – Li Pointeurs et passage par référence correction void compte(char tab[], int taille, char c, int *nb) { int i; *nb = 0; for(i=0; i < taille; i++) { if (tab[i] == c) { *nb = *nb + 1; } } } Soient les déclarations suivantes : char mot[5]={'m','a','m','a','n'}; int compteur; et l'appel compte(mot,5,'m',&compteur);
CINI – Li m Pointeurs et passage par référence correction void compte(char tab[], int taille, char c, int *nb) { int i; *nb = 0; for(i=0; i < taille; i++) { if (tab[i] == c) { *nb = *nb + 1; } } } char mot[5]={'m','a','m','a','n'}; int compteur; compte(mot,5,'m',&compteur); ? compteur mot m m a tab 5 taille 'm' c ? nb a man
CINI – Li Arithmétique des pointeurs ● Qu'affiche le programme suivant ? #include int main(){ char * tabC = "bonjour"; CINI_print_char(*tabC); CINI_newline(); CINI_print_char(*(tabC+3)); CINI_newline(); tabC = tabC + 2; CINI_print_char(*tabC); CINI_newline(); CINI_print_char(*(tabC+3)); CINI_newline(); return 0; } bjnubjnu correction