Passare matrici e matrici a funzioni come puntatori e puntatori ai puntatori in C

Dato il seguente codice:

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

Non capisco perché ricevo questo avvertimento:

warning: passaggio dell’argomento 1 di ‘bar’ dal tipo di puntatore incompatibile

Anche se la chiamata ‘pippo’ sembra essere ok.

Grazie 🙂

Beh, certamente non è ben compreso dalla comunità C come può essere visto dando un’occhiata a SO. La magia è che tutti i seguenti sono totalmente equivalenti al 100% :

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

È molto importante tracciare la distinzione tra un puntatore e un array. Un array non è un puntatore . Un array può essere convertito in un puntatore al suo primo elemento. Se hai un puntatore hai questo:

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

Tuttavia, se si dispone di un array, si ha questo:

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

Con il puntatore, i dati si trovano su un intero altro pianeta, ma collegati dal puntatore. Un array ha i dati stessi. Ora, un array multidimensionale è solo una serie di array. Gli array sono nidificati in un array principale. Quindi, la dimensione del tuo array è:

 (sizeof(int) * 10) * 10 

Questo perché hai 10 matrici, che sono tutte matrici di 10 numeri interi. Ora, se vuoi passare quell’array, viene convertito. Ma a cosa? Un puntatore al suo primo elemento. Il tipo di elemento non è un puntatore, ma una matrice. Di conseguenza, si passa un puntatore a un array di 10 int:

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

Non è né un array di int* né un int** . Potresti chiedere perché l’array non è passato come int** . È perché il compilatore deve conoscere la lunghezza della riga. Se si esegue un array[1][0] , il compilatore indirizzerà una dimensione del luogo di sizeof(int) * 10 byte a parte dall’inizio dell’array bidimensionale. Decodifica le informazioni nel tipo puntatore-array.

Quindi, devi scegliere tra uno dei prototipi di funzioni completamente equivalenti di cui sopra. Naturalmente, l’ultimo è solo confuso. Il compilatore ignora silenziosamente qualsiasi numero scritto nella dimensione più esterna se un parametro viene dichiarato come array. Quindi non utilizzerei nemmeno la penultima versione. La cosa migliore è usare la prima o la seconda versione. Ciò che è importante ricordare è che C non ha parametri di array (reali) ! Il parametro sarà un puntatore alla fine (puntatore all’array in questo caso).

Nota come il caso multidimensionale di sopra è simile al caso degenerato, un caso dimensionale di seguito. Tutte le seguenti 4 versioni sono completamente equivalenti:

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

Passare array multidimensionali in C è un argomento delicato. Vedi questa FAQ .

La domanda da porsi è come userete la bar . Se sai sempre che verrà passato un array 10×10, allora riscrivilo come

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

Se vuoi far fronte a matrici di dimensioni diverse, potresti dover passare le lunghezze:

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

Il problema è che la matrice della struttura dei dati [10] [10] non è in realtà una tabella di dieci puntatori agli array [10], ma è una matrice sequenziale di 100 interi. La firma corretta per bar è

 bar (int matrix[10][10]) 

Se in realtà si desidera rappresentare la matrice utilizzando l’indirezione e avere int ** matrix come tipo di parametro per la barra, è necessario allocarlo in modo diverso:

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

Ora 'matrice' corrisponde al tipo int **. 'matrice' è una matrice di dieci puntatori e puoi passarla per puntatore, ottenendo così il secondo *.

Ecco un codice su cui esercitarsi: contiene tutti i possibili tipi di array e codice 2D bidimensionale per accedere ai valori degli elementi

 #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 

Dovresti definire una barra come:

 bar( int* matrix ) 

In C tutti gli array devono essere passati come int* (o type_of_element* per altri tipi).

int ** sarebbe ok se i tuoi dati fossero davvero una serie di puntatori. int[*data[] per esempio. Ecco cosa si ottiene in main(int argc, char *argv[]) .

 int **matrix 

indicherebbe che hai un puntatore a un puntatore a int. Questo è comunemente usato per indicare un puntatore a un array di puntatori (chiamato anche vettore). Questo NON è assolutamente il caso

 int matrix[10][10] 

che è più un puntatore a una singola sezione di memoria dimensionata per 10×10 pollici. Prova a cambiare a:

 void bar(int *matrix[])