Comment passer une méthode à la hauteur et à la largeur d’un tableau 2D en tant que parameters?

Disons que j’ai un tableau: array[2][4] , et à l’intérieur de la méthode principale, j’ai un appel à une fonction blackandwhite . Comment puis-je transmettre à cette méthode la longueur et la largeur du tableau en tant qu’arguments?

C’est une solution possible:

 void blackandwhite(int* array, int height, int width) { // Array-processing done here. // array is pointer to int, // initially points to element myarray[0][0]. // variable height = 2; // variable width = 4; } int main() { int myarray[2][4]; blackandwhite(&myarray[0][0], 2, 4); } 

On peut trouver la taille d’un tableau, c’est-à-dire le nombre d’éléments qu’il contient, à l’aide de la construction suivante:

 int array[8]; int size = sizeof(array)/sizeof(array[0]); 

Malheureusement, les tableaux C sont des tableaux natifs et ne contiennent aucune métadonnée incorporée. Les lignes et les colonnes ne sont qu’un moyen de représenter / d’accéder à un espace de stockage essentiellement linéaire en mémoire. Si je comprends bien, il n’existe aucun moyen de déterminer automatiquement le nombre de lignes / colonnes d’un tableau 2D, à partir du pointeur correspondant (en C).

Par conséquent, il est nécessaire de transmettre le nombre de colonnes / lignes sous forme d’arguments séparés avec le pointeur sur le tableau 2D, comme illustré dans l’exemple ci-dessus.

Plus d’infos sur une question connexe ici .


METTRE À JOUR:

Piège commun n ° 1: Utilisation du int** array dans la liste de parameters
Notez qu’un pointeur sur un tableau d’entiers à 2 dimensions est toujours un pointeur sur un int.
int** implique que le paramètre se réfère à un pointeur vers un pointeur vers un int, ce qui n’est PAS le cas ici.

Piège commun n ° 2: utiliser int[][] dans la liste de parameters
Échec de la transmission des dimensions du tableau. On n’a PAS besoin de passer la taille de la 1ère dimension du tableau (mais vous pouvez mais le compilateur l’ignorera). Les dimensions de fin sont obligatoires cependant. Alors,

 // is INVALID! void blackandwhite(int array[][], int height, int width) // is VALID, 2 is ignored. void blackandwhite(int array[2][4], int height, int width) // is VALID. void blackandwhite(int array[][4], int height, int width) 

Si vous utilisez un compilateur C99 ou un compilateur C2011 qui prend en charge les tableaux de longueur variable, vous pouvez effectuer les opérations suivantes:

 /** * the cols parameter must be declared before it is used in * the array parameter. */ void blackandwhite(size_t rows, size_t cols, int (*array)[cols]) { size_t i, j; for (i = 0; i < rows; i++) for (j = 0; j < cols; j++) array[i][j] = ...; } 

et vous l'appelleriez comme

 int array[N][M]; ... blackandwhite(N, M, array); 

Dans la plupart des cas 1 , une expression de type "tableau à N éléments de T " sera convertie ("decay") en une expression de type "pointeur sur T ", et la valeur de l'expression sera l'adresse du premier élément dans le tableau. Le array expression a le type "tableau N élément de M tableau de int "; lorsque nous le transmettons en tant que paramètre à blackandwhite , il est converti en une expression de type "pointeur sur M tableau de int ", ou int (*)[M] , et sa valeur est identique à &array[0] .

Si vous utilisez un compilateur C2011 qui ne prend pas en charge les tableaux de longueur variable (le support VLA est maintenant facultatif), ou un compilateur C89 ou antérieur (qui n'a jamais pris en charge les VLA), vous avez le choix entre deux options: vous pouvez coder en dur le nombre de colonnes:

 void blackandwhite(size_t rows, int (*array)[M]) { size_t i,j; for (i = 0; i < rows; i++) for (j = 0; j < M; j++) array[i][j] = ...; } ... blackandwhite(N, array); 

Dans ce cas, cette fonction fonctionnera uniquement avec les tableaux contenant un nombre spécifique de colonnes. Vous pouvez également utiliser l'approche présentée par theCodeArtist, dans laquelle vous passez explicitement un pointeur sur le premier élément du tableau avec les lignes et les colonnes en tant que parameters. Cependant, cela signifie que vous traiterez un array comme un tableau 1D, et non comme un tableau 2D, ce qui signifie que vous devrez mapper des index 2D sur une structure 1D:

 void blackandwhite(int *array, size_t rows, size_t cols) { size_t i, j; for (i = 0; i < rows; i++) for (j = 0; j < cols; j++) array[i * rows + j] = ...; // use a 1D index } ... blackandwhite(&array[0][0], N, M); 

Notez que cette approche repose sur le fait que toutes les lignes et colonnes du array sont contiguës en mémoire. si vous aviez alloué dynamicment array tel que

 int **array = malloc(N * sizeof *array); if (array) { size_t i; for (i = 0; i < N; i++) array[i] = malloc(M * sizeof *array[i]); } 

alors il n'y a aucune garantie que les lignes sont disposées de manière contiguë en mémoire. Cependant, dans ce cas particulier , vous pourriez écrire ce qui suit:

 void blackandwhite(int **array, size_t rows, size_t cols) { size_t i, j; for (i = 0; i < rows; i++) for (j = 0; j < cols; j++) array[i][j] = ...; } ... blackandwhite(array, N, M); 

Encore confus?

Rappelez-vous que l'expression a[i] est traitée comme *(a + i) ; c'est-à-dire que nous trouvons l'adresse du i ème élément après a et déréférencons la valeur du pointeur obtenue. a[i][j] est interprété comme *(*(a+i)+j) ; *(a + i) nous donne le ième tableau suivant a ; Selon la règle mentionnée ci-dessus, cette expression est convertie de type tableau en type pointeur et la valeur correspond à l'adresse du premier élément. Nous ajoutons ensuite j à la nouvelle valeur du pointeur et déréférencons à nouveau le résultat.

Nous pouvons utiliser array[i][j]array est passé soit comme pointeur sur un tableau ( int (*array)[cols] ou int (*array)[M] ), soit comme pointeur sur un pointeur ( int **array ) car le résultat de array[i] est soit un type de pointeur, soit un type de tableau qui se décompose en un type de pointeur, auquel l'opérateur de l'indice peut être appliqué. Nous ne pouvons pas utiliser array[i][j]array est passé sous forme de simple pointeur sur int , car dans ce cas, le résultat de array[i] n'est pas un type de pointeur.

Alors pourquoi ne pas faire

 void blackandwhite(int **array, size_t rows, size_t cols) {...} ... int array[N][M]; blackandwhite((int **) array, N, M); 

Les pointeurs et les tableaux sont des choses différentes, donc un pointeur sur un pointeur et un pointeur sur un tableau sont des choses différentes. Cela fonctionnerait probablement , mais vous mentez au compilateur. Au-dessus de tout le rest, c'est la mauvaise forme.

MODIFIER

Dans le contexte d'une déclaration de paramètre de fonction, une déclaration de paramètre de forme T a[] ou T a[N] sera interprétée comme T *a ; IOW, a sera déclaré comme un pointeur, pas un tableau. De même, les déclarations de parameters de la forme T a[N][M] ou T a[][M] sont traitées comme T (*a)[M] ; encore a fois, a est déclaré comme un pointeur, pas un tableau.

Dans notre premier exemple, nous aurions pu déclarer blackandwhite

 void blackandwhite(size_t rows, size_t cols, int array[rows][cols]) 

ou

 void blackandwhite(size_t rows, size_t cols, int array[][cols]) 

mais je préfère utiliser explicitement les déclarations de pointeur, car elles reflètent correctement ce qui se passe.


1. Les exceptions à cette règle _Alignof lorsque l'expression de tableau est un opérande des opérateurs sizeof , _Alignof ou _Alignof & , ou qu'un littéral de chaîne est utilisé pour initialiser un autre tableau dans une déclaration, tel que

 char str[]="This is a test"; 

.

En C, il est connu qu’il existe une méthode pour calculer la longueur d’une chaîne, alors que dans le cas d’un tableau, vous devez spécifier explicitement les dimensions du tableau pour le passage de la longueur et de la largeur à la fonction blackandwhite .