web-dev-qa-db-fra.com

Pourquoi avons-nous besoin de spécifier la taille de la colonne lors du passage d'un tableau 2D en tant que paramètre?

Pourquoi mon paramètre ne peut-il pas être

void example(int Array[][]){ /*statements*/}

Pourquoi dois-je spécifier la taille de colonne du tableau? Dis par exemple, 3

void example(int Array[][3]){/*statements*/}

Mon professeur a dit que c'était obligatoire, mais je codais avant le début des cours et je me suis rappelé qu'il n'y avait pas d'erreur syntaxique ou sémantique lorsque j'ai fait de ceci mon paramètre? Ou ai-je raté quelque chose? 

32
Jan Tristan Milan

Lorsqu'il s'agit de décrire des paramètres, les tableaux se décomposent toujours en pointeurs vers leur premier élément.

Lorsque vous transmettez un tableau déclaré en tant que int Array[3] à la fonction void foo(int array[]), il se décompose en un pointeur situé au début du tableau, c.-à-d. int *Array;. Btw, vous pouvez décrire un paramètre comme int array[3] ou int array[6] ou même int *array - tous ceux-ci seront équivalents et vous pouvez passer n'importe quel tableau entier sans problèmes.

Dans le cas de tableaux de tableaux (tableaux 2D), il se décompose également en un pointeur vers son premier élément, qui se trouve être un tableau unidimensionnel, c'est-à-dire que nous obtenons int (*Array)[3]

Spécifier la taille ici est important. Si ce n'était pas obligatoire, le compilateur n'aura aucun moyen de savoir comment traiter l'expression Array[2][1], par exemple. 

Pour déréférencer qu'un compilateur a besoin de calculer le décalage de l'élément, nous avons besoin d'un bloc de mémoire contigu (int Array[2][3] est un bloc d'entiers contigu), ce qui devrait être facile pour les pointeurs. Si a est un pointeur, alors a[N] est développé en tant que start_address_in_a + N * size_of_item_being_pointed_by_a. Dans le cas de l'expression Array[2][1] à l'intérieur d'une fonction (nous voulons accéder à cet élément), la variable Array est un pointeur sur un tableau à une dimension et la même formule s'applique. Le nombre d'octets dans le dernier crochet est nécessaire pour trouver size_of_item_being_pointed_by_a. Si nous avions juste Array[][], il serait impossible de le trouver et donc de déréférencer un élément de tableau dont nous avons besoin.

Sans la taille, les arithmétiques de pointeurs ne fonctionneraient pas pour les tableaux de tableaux. Quelle adresse Array + 2 doit-il produire: avance l'adresse dans Array 2 octets à l'avance (faux) ou avance le pointeur 3* sizeof(int) * 2 octets à venir?

30
Maksim Skurydzin

En C/C++, même les tableaux 2D sont stockés séquentiellement, une ligne après l'autre, en mémoire. Donc, quand vous avez (dans une seule fonction):

int a[5][3];
int *head;

head = &a[0][0];
a[2][1] = 2; // <--

L'élément auquel vous accédez réellement avec a[2][1] est *(head + 2*3 + 1), car séquentiellement, cet élément se trouve après 3 éléments de la ligne 0 et 3 éléments de la ligne 1, puis un autre index supplémentaire.

Si vous déclarez une fonction comme:

void some_function(int array[][]) {...}

syntaxiquement, cela ne devrait pas être une erreur. Mais, lorsque vous essayez d'accéder à array[2][3] maintenant, vous ne pouvez pas savoir quel élément est censé être utilisé. Par contre, quand vous avez:

void some_function(int array[][5]) {...}

vous savez qu'avec array[2][3], il est possible de déterminer que vous accédez réellement à l'élément à l'adresse mémoire *(&array[0][0] + 2*5 + 3)car la fonction connaît la taille de la deuxième dimension.

Il y a une autre option, comme suggéré précédemment, vous pouvez déclarer une fonction comme:

void some_function(int *array, int cols) { ... }

parce que de cette façon, vous appelez la fonction avec les mêmes "informations" qu'auparavant - le nombre de colonnes. Vous accédez alors un peu différemment aux éléments du tableau: vous devez écrire *(array + i*cols + j) où vous écrivez habituellement array[i][j], car array est maintenant un pointeur sur entier (et non sur un pointeur).

Lorsque vous déclarez une fonction comme celle-ci, vous devez faire attention à l'appeler avec le nombre de colonnes réellement declarées pour le tableau, et pas seulement utilisées. Donc, par exemple:

int main(){
   int a[5][5];
   int i, j;

   for (i = 0; i < 3; ++i){
       for (int j=0; j < 3; ++j){
           scanf("%d", &a[i][j]);
       }
   }

   some_function(&a[i][j], 5); // <- correct
   some_function(&a[i][j], 3); // <- wrong

   return 0;
}
11
penelope

Il y a un post similaire à ce sujet. Vous pouvez vous référer au lien ci-dessous . Création d'un tableau en C et passage d'un pointeur au tableau pour qu'il fonctionne J'espère que cela vous aidera.

D'autre part, le compilateur a besoin de la deuxième dimension pour pouvoir déplacer "Tableau" d'un pointeur à l'autre car la mémoire entière est disposée de manière linéaire.

0
Manik Sidana