Bonjour, pourquoi je peux pas faire :
Autre question sans rapport : pourquoi je peux pas faire :Code:
1
2
3
4
5
6
7 void foo(int **tab) { } int tab[5][10]; foo(tab); /* erreur */
?Code:int tab[][] = { {1, 1}, {1, 1} };
Merci.
Version imprimable
Bonjour, pourquoi je peux pas faire :
Autre question sans rapport : pourquoi je peux pas faire :Code:
1
2
3
4
5
6
7 void foo(int **tab) { } int tab[5][10]; foo(tab); /* erreur */
?Code:int tab[][] = { {1, 1}, {1, 1} };
Merci.
Parce que un tableau, même en plusieurs dimensions, n'est traduit en mémoire qu'en une seule dimension (une suite de cases). Donc pour aller sur l'élément tab[x][y], le C est obligé de traduire ça en "case mémoire située à l'emplacement x * 10 + y".
Or, dans la fonction foo, l'élément "tab" n'est plus qu'un pointeur et le C ne connait pas sa largeur "10"
Code:
1
2
3 void foo(int *tab[10]) { }
Même réponse. Tu n'as le droit de n'omettre que la dernière des dimensions (celle qui est la plus proche du nom de variable). Le compilo utilise toutes les autres plus ta déclaration pour la calculer.
Code:int tab[][2] = { {1, 1}, {1, 1} };
Le deuxième indice ne peut pas être omis.
essaye
où y = deuxième indice.Code:void foo(int **tab, int y)
Le compilateur ne connait pas la taille maximale du deuxième indice.Code:int tab[][] = { {1, 1}, {1, 1} };
Sve@r >> Ce ne serait pas plutôt void foo(int tab[][10]) ?
Ah non. Le problème vient de la transformation de "[m][n]" en "m * largeur + n". Et cette transformation est faite par le compilo. Et le compilo ne sait pas que le second paramètre est la largeur qu'il attend...
Ouaip. Mais comme la dernière dimension peut être remplacée par une étoile, j'ai voulu me rapprocher le plus possible de l'écriture initiale...
:roll: :oops:
Il me semble que ce n'est pas char *tab[MAXY], mais char (*tab)[MAXY]...
Et comment le C fait pour les pointeurs alors ?
Si je fais :
int **tab = ...;
tab[x][y] = 2;
Il sait où il faut aller pourtant.
Je ne vois pas pourquoi il ne peut pas retrouver les dimensions. Il a tous les éléments pour le faire (2 x {} et dans chaque {}, 2 élements -> tableau [2][2]).
Pas du tout, l'équivalence a lieu entre:
etCode:
1
2 void foo(char (*tab)[MAXY]); /* -tc- les parentheses sont importantes! */
qui ne sont absolument pas équivalent à:Code:void foo(char tab[][MAXY]);
Dans les deux premier cas, le paramètre tab est de type pointeur sur un tableau de MAXY chars, tandis que dans le 3ème cas, tab est de type pointeur sur pointeur sur char.Code:void foo(char **tab, size_t y);
Thierry
Parce que tab[x][y] est implicitement converti en:
où tab est de type pointeur sur pointeur sur int. En C, tableaux et pointeurs sont différents. Par contre, il y a équivalence entre l'arthimétique des pointeurs et l'accès par indiçage (c'est français ça?) à un tableau.Code:
1
2 *(*(tab + x) + y);
Thierry
OK.
En fait un tableau pur est linéaire.
Alors qu'un pointeur de pointeurs non.
C'est ce qu'avait dit Sve@r.
Comment ai-je pu oublier ?
Par contre ma deuxième question tient toujours.
Oui. Mais
- sois tu fais "tab=autre_tableau" avec "autre_tableau" défini comme il faut. Donc il y a équivalence entre "tab" et "autre_tableau" => le C sais adresser "tab[x]|y]" puisqu'il se base sur les dimensions de "autre_tableau" pour le faire
- sois tu fais "tab=malloc(n * sizeof(*tab))" puis "for (x=0; x < n; x++) tab[x]=malloc(m * sizeof(**tab)". Et là, le C sait aller sur tab[x] qui est le "xième" pointeur pris à partir de tab puis, de tab[x], il sait se déplacer sur l'élément [y] qui est le "yième" élément pris à partir de tab[x]. Autrement dit tab[x][y] n'est qu'une double indirection.
Dans cette dernière écriture, il n'y a aucune obligation que l'ensemble du tableau soit contigü. T'as simplement tab qui contient un tableau de pointeurs (qui se suivent) mais chaque pointeur pointe vers une zone de la mémoire totalement diverse (et pas forcément aux mêmes endroits).
Toi tu sais le faire. Moi aussi. Mais le compilo qui n'est qu'un simple programme qui n'a pas été fait pour ça, ne sait pas le faire.
Inversement, si on voulait programmer un compilo qui sache le faire, il faudrait lui introduire un algorithme récursif (vu qu'on ne saurait pas à l'avance le nombre de dimensions max) pour analyser l'écriture.
Parce qu'un tableau deux dimensions, ça n'existe pas réellement en C. Lorsque tu définis:
Tu définis un tableau de 3 tableaux de 4 ints. Ainsi, ce n'est rien d'autre qu'un tableau à une dimension contenant des éléments de type tableau de 4 int. Lorsque tu définis ton tableau, tu ne peux pas écrire:Code:int tab[3][4];
,Code:int tab[][] = {{1,1}, {1,1}, {1,1}}; /* -tc- ATTENTION: Ceci est faux! */
Car le type des éléments du tableaux n'est pas défini. Tu dois donc écrire:
qui correspond à la forme:Code:
1
2 int tab[][2] = {{1,1}, {1,1}, {1,1}};
où type est un tableau de 2 ints.Code:
1
2 type identificateur_tableau[] = initialisation
Thierry
Sve@r :
Il n'a absolument pas besoin de se baser sur les dimensions de quelque chose d'autre. tab[x] est un int * et il connait la taille d'in int * donc il peut situer tab[x]. tab[x][y] est un int. Il sait situer tab[x], il connait la taille d'un int ; il peut situer tab[x][y]Citation:
Oui. MaisCitation:
Et comment le C fait pour les pointeurs alors ?
Si je fais :
int **tab = ...;
tab[x][y] = 2;
Il sait où il faut aller pourtant.
- sois tu fais "tab=autre_tableau" avec "autre_tableau" défini comme il faut. Donc il y a équivalence entre "tab" et "autre_tableau" => le C sais adresser "tab[x]|y]" puisqu'il se base sur les dimensions de "autre_tableau" pour le faire
....
Parce qu'un pointeur de pointeur n'a rien à voir avec un tableau linéaire à 2 dimensions. Tu confonds avec les tableaux dynamiques à 2 dimensions formés d'un tableau de pointeurs et de tableaux 'accrochés' à ces pointeurs...
Parce que le langage C ne l'autorise pas. Il autoriseCitation:
Autre question sans rapport : pourquoi je peux pas faire :
?Code:int tab[][] = { {1, 1}, {1, 1} };
etc. Mais pasCode:
1
2
3
4 int tab[4][5] = { {1, 1}, {1, 1} }; int tab[][5] = { {1, 1}, {1, 1} }; int tab[2][2] = { {1, 1}, {1, 1} }; int tab[][2] = { {1, 1}, {1, 1} };
niCode:int tab[4][] = { {1, 1}, {1, 1} };
Code:int tab[][] = { {1, 1}, {1, 1} };
Tu confonds tableaux et pointeurs.
déclare un tableau de 10 entiers. Le type de t est int [10] (tableau de 10 entiers). Ici, sizeof(t) = 10 * sizeof(int).Code:int t[10];
déclare un tableau de 5 int [10]. En mémoire c'est tout simplement donc un tanleau de 50 int. Un tableau à deux dimensions est un tableau, pas un pointeur de pointeurs. Ici sizeof(t) = 5 * sizeof(int [10]) = 5 * 10 * sizeof(int). Un int * (mais pas un int **) permet donc de pointer sur t, mais un cast est nécessaire.Code:int t[5][10];
déclare un tableau de 5 pointeurs (int *). Ici, sizeof(t) = 5 * sizeof(int *).Code:int * t[5];
On peut aussi initialiser un tableau pendant sa déclaration. Par exemple :
Ceci est strictement équivalent à :Code:int t[10] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
Un autre exemple :Code:int t[] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
Est équivalent à :Code:
1
2
3
4
5
6
7 int t[5][10] = { {0, 10, 20, 30, 40, 50, 60, 70, 80, 90}, {1, 11, 21, 31, 41, 51, 61, 71, 81, 91}, {2, 12, 22, 32, 42, 52, 62, 72, 82, 92}, {3, 13, 23, 33, 43, 53, 63, 73, 83, 93}, {4, 14, 24, 34, 44, 54, 64, 74, 84, 94} };
L'initialisation d'un tableau peut être icomplète dans ce cas les autres éléments sont tous initialisés à zéro. Par exemple :Code:
1
2
3
4
5
6
7 int t[][10] = { {0, 10, 20, 30, 40, 50, 60, 70, 80, 90}, {1, 11, 21, 31, 41, 51, 61, 71, 81, 91}, {2, 12, 22, 32, 42, 52, 62, 72, 82, 92}, {3, 13, 23, 33, 43, 53, 63, 73, 83, 93}, {4, 14, 24, 34, 44, 54, 64, 74, 84, 94} };
Code:int t[10] = {0, 10, 20, 30, 40};
En résumé, on a, il me semble, deux situations :
- on connaît les dimensions du tableau : autant le définir proprement avec par exemple :
- on ne connaît pas les dimensions : on va utiliser des tableaux dynamiques, et donc par définition on ne peut pas initialiser le tableau lors de sa déclaration.Code:
1
2 int MonTableau[5][10] /* et éventuellement ={...} */
Essayer de mélanger les deux n'a guère de sens.