Passer des tableaux et des masortingces à des fonctions en tant que pointeurs et des pointeurs à des pointeurs en C

Étant donné le code suivant:

void foo( int* array ) { // ... } void bar( int** masortingx ) { // ... } int main( void ) { int array[ 10 ]; int masortingx[ 10 ][ 10 ]; foo( array ); bar( masortingx ); return 0; } 

Je ne comprends pas pourquoi je reçois cet avertissement:

warning: passage de l’argument 1 de ‘bar’ à partir d’un type de pointeur incompatible

Bien que “foo” appelle semble être ok.

Merci 🙂

Eh bien, ce n’est certainement pas bien compris par la communauté C, comme on peut le voir en regardant SO. La magie est, tout ce qui suit est totalement, 100%, équivalent :

 void foo(int (*array)[10]); void foo(int array[][10]); void foo(int array[10][10]); void foo(int array[42][10]); 

Il est très important de faire la distinction entre un pointeur et un tableau. Un tableau n’est pas un pointeur . Un tableau peut être converti en un pointeur sur son premier élément. Si vous avez un pointeur, vous avez ceci:

 -------- | ptr | -------> data -------- 

Cependant, si vous avez un tableau, vous avez ceci:

 --------------------------- | c1 | c2 | c3 | ... | cn | --------------------------- 

Avec le pointeur, les données sont sur une autre planète, mais liées par le pointeur. Un tableau a les données elles-mêmes. Maintenant, un tableau multidimensionnel est juste un tableau de tableaux. Les tableaux sont nesteds dans un tableau parent. La taille de votre tableau est donc:

 (sizeof(int) * 10) * 10 

C’est parce que vous avez 10 tableaux, qui sont tous des tableaux de 10 entiers. Maintenant, si vous voulez passer ce tableau, il est converti. Mais à quoi? Un pointeur sur son premier élément. Le type d’élément n’est pas un pointeur, mais un tableau. En conséquence, vous passez un pointeur sur un tableau de 10 int:

 int (*)[10] // a pointer to an int[10] 

Ce n’est ni un tableau d’ int* , ni un int** . Vous pouvez vous demander pourquoi le tableau n’est pas passé en tant que int** . C’est parce que le compilateur doit connaître la longueur de la ligne. Si vous faites un array[1][0] , le compilateur adressera un espace sizeof(int) * 10 octets en dehors du début du tableau à 2 dimensions. Il décode ces informations dans le type pointeur vers tableau.

Vous devez donc choisir parmi l’un des prototypes de fonctions totalement équivalents ci-dessus. Naturellement, le dernier est déroutant. Le compilateur ignore simplement tout nombre écrit dans la dimension la plus externe si un paramètre est déclaré être un tableau. Donc, je ne voudrais pas non plus utiliser l’avant dernière version. Le mieux est d’utiliser la première ou la deuxième version. Ce qui est important à retenir est que C n’a pas de parameters de tableau (réels) ! Le paramètre sera un pointeur à la fin (pointeur vers tableau dans ce cas).

Notez comment le cas multi-dimensionnel ci-dessus est similaire au cas dégénéré, un cas dimensionnel ci-dessous. Les 4 versions suivantes sont totalement équivalentes:

 void foo(int *array); void foo(int array[]); void foo(int array[10]); void foo(int array[42]); 

Passer des tableaux multidimensionnels en C est un sujet délicat. Voir cette FAQ .

La question à poser est de savoir comment vous allez utiliser bar . Si vous savez toujours qu’il va passer un tableau 10×10 puis le réécrire comme

 bar(int masortingx[10][10]); 

Si vous souhaitez gérer des tableaux de dimensions variables, vous devrez peut-être indiquer les longueurs suivantes:

 bar(int *masortingx, int width, int height); 

Le problème est que la masortingce de structure de données [10] [10] n’est en réalité pas une table de dix pointeurs vers le tableau [10], mais un tableau séquentiel de 100 entiers. La signature appropriée pour le bar est

 bar (int masortingx[10][10]) 

Si vous voulez réellement représenter la masortingce en utilisant indirection et utiliser int ** masortingx comme type de paramètre pour bar, vous devez l’affecter différemment:

 int *masortingx[10]; int my_data[100]; int i; for (i = 0; i < 10; i++) { matrix[i] = &(my_data[i * 10]); } bar(matrix); 

Maintenant 'masortingce' correspond au type int **. 'masortingce' est un tableau de dix pointeurs que vous pouvez transmettre par pointeur, obtenant ainsi le second *.

Voici un code sur lequel s’exercer – il contient tous les types possibles de passage de tableau à deux dimensions et de code permettant d’accéder aux valeurs des éléments.

 #include  #define NUMROWS 2 #define NUMCOLUMNS 5 #define FILL_ARRAY() \ *array[0] = '1'; \ (*array)[7] = '2'; \ *(array[1]) = '3'; \ *(*(array+1)+1) = '4'; \ *(array[0]+3) = '5'; \ *(*array+2) = '7'; \ array[0][1] = '6'; void multi_01( char (*array)[NUMCOLUMNS] ) { FILL_ARRAY(); } void multi_02( char array[][NUMCOLUMNS] ) { FILL_ARRAY(); } void multi_03( char array[NUMROWS][NUMCOLUMNS] ) { FILL_ARRAY(); } void multi_04( char **array ) { FILL_ARRAY(); } void multi_05( char *array[] ) { FILL_ARRAY(); } void multi_06( char *array[NUMCOLUMNS] ) { FILL_ARRAY(); } int main(int argc, char **argv) { int i; char mystr[NUMROWS][NUMCOLUMNS] = { { 'X', 'X', 'X', 'X'}, {'X','X','X'} }; char *pmystr[sizeof(mystr)/sizeof(*mystr)]; int numcolumns = sizeof(*mystr); int numrows = sizeof(mystr)/sizeof(*mystr); for( i=0; i 

Vous devez définir bar comme:

 bar( int* masortingx ) 

En C, tous les tableaux doivent être passés en tant que int* (ou type_of_element* pour les autres types).

int ** serait ok si vos données étaient vraiment un tableau de pointeurs. int[*data[] par exemple. C’est ce que vous obtenez dans main(int argc, char *argv[]) .

 int **masortingx 

indiquerait que vous avez un pointeur sur un pointeur sur int. C’est couramment utilisé pour indiquer un pointeur sur un tableau de pointeurs (également appelé vecteur). Ce n’est certainement pas le cas avec

 int masortingx[10][10] 

qui est plus d’un pointeur sur une seule section de mémoire dimensionnée pour 10×10 ints. Essayez de changer pour:

 void bar(int *masortingx[])