2009-02-13 20 views
14

Dato il seguente codice:Passando array e matrici a funzioni come puntatori e puntatori ai puntatori in C

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 il motivo per cui ottengo questo avvertimento:

avvertimento: passando argomento 1 di 'bar' dal tipo di puntatore incompatibile

Anche se la chiamata 'pippo' sembra essere ok.

Grazie :)

risposta

43

Beh, non è certamente ben compreso dalla comunità C come si può vedere dando un'occhiata a SO.La magia è, tutti i seguenti sono totalmente, 100%, equivalente:

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

È molto importante della distinzione di un puntatore e un array. Un array non è un puntatore. Un array può essere convertito in un puntatore al suo primo elemento. Se si dispone di un puntatore avete questo:

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

Tuttavia, se si dispone di una matrice, è avere questo:

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

Con il puntatore, il dato è al tutto un altro pianeta, ma collegato a dal puntatore. Un array ha i dati stessi. Ora, un array multidimensionale è solo un array di matrici. Gli array sono nidificati in un array principale. Così, il sizeof della matrice è:

(sizeof(int) * 10) * 10 

Questo perché si dispone di 10 array, che sono tutti array di 10 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 una matrice di 10 int:

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

Non è né una matrice di int*, né una 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à un posto a sizeof(int) * 10 byte a parte dall'inizio della matrice bidimensionale. Decodifica le informazioni nel tipo puntatore-array.

Quindi, è necessario 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 cui sopra è simile al caso degenerato, un caso dimensionale di seguito. Tutti i seguenti 4 versioni sono del tutto equivalente:

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

Sebbene la risposta di Mark Pim sia centrata sul problema principale della mia domanda, voglio compensare in qualche modo tutto il tuo sforzo nel tentativo di spiegare le sottofinestre degli array come parametri in C impostando questa risposta come raccomandato. Grazie! – Auron

1

È necessario definire bar:

bar(int* matrix) 

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

int ** sarebbe ok se i tuoi dati erano davvero una serie di puntatori. int[*data[] per esempio. Questo è quello che ottieni in main(int argc, char *argv[]).

6

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

bar (int matrix[10][10]) 

Se si vuole realmente rappresentare la matrice utilizzando indirezione e hanno int ** matrice come il tipo di parametro per bar, allora avete bisogno di allocare 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 *.

+0

"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 numeri interi. " Ecco perché ero così confuso! – Auron

14

Il passaggio di matrici multidimensionali in C è un argomento delicato. Vedi this FAQ.

La domanda da porsi è come userete bar. Se sai sempre che sarà passata una matrice 10x10 poi riscriverlo come

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

Se si vuole far fronte con array di dimensioni variabili, allora potrebbe essere necessario passare nelle lunghezze:

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

La tua è una grande risposta. Grazie! La barra – Auron

-1
int **matrix 

indica che si ha un puntatore a un puntatore a int. Questo è comunemente usato per indicare un puntatore a un array di puntatori (chiamato anche vettore). Questo è sicuramente non il caso con

int matrix[10][10] 

che è un più di un puntatore a una singola sezione della memoria dimensionato per 10x10 int. Provare a cambiare:

void bar(int *matrix[]) 
+0

valuta la matrice int ** e non è come passerebbe la matrice – Ulterior

2

Ecco il codice di pratica su - contiene tutti i possibili tipi di passaggio di matrice e codice 2dimensional accedere elemento valori

#include <stdio.h> 

#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<numrows; i++) pmystr[i] = *(mystr+i); 

    multi_01(mystr); multi_02(mystr); multi_03(mystr); 
    multi_04(pmystr); multi_05(pmystr); multi_06(pmystr); 

    printf("array '%s', '%s'\n", mystr[0], mystr[1]); 

    getc(stdin); 
    return 0; 
}