2009-02-13 13 views
14

mit dem folgenden Code Passing:Arrays und Matrizen Funktionen als Zeiger und Zeiger auf Zeiger 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; 
} 

Ich verstehe nicht, warum ich diese Warnung erhalten:

Warnung: Argument übergeben 1 von 'bar' vom inkompatiblen Zeigertyp

Obwohl 'foo' Anruf scheint in Ordnung zu sein.

Thanks :)

Antwort

43

Nun, es ist sicherlich nicht gut von der C-Community verstanden, wie durch einen Blick über SO gesehen werden kann.Die Magie ist, alle folgenden sind total 100%, das entspricht:

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

Es ist sehr wichtig, die Unterscheidung eines Zeigers, und einer Anordnung zu ziehen. Ein Array ist kein Zeiger. Ein Array kann in einen Zeiger auf sein erstes Element konvertiert werden. Wenn Sie einen Zeiger haben Sie diese haben:

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

Wenn Sie jedoch ein Array haben, haben Sie dies:

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

Mit dem Zeiger werden die Daten auf einem ganz anderen Planeten, sondern verknüpft durch den Zeiger. Ein Array enthält die Daten selbst. Jetzt ist ein mehrdimensionales Array nur ein Array von Arrays. Die Arrays sind in einem übergeordneten Array verschachtelt. Also, die sizeof Ihres Arrays ist:

(sizeof(int) * 10) * 10 

Das ist, weil Sie 10 Arrays, von denen alle Arrays von 10 ganzen Zahlen sind. Wenn Sie dieses Array übergeben möchten, wird es konvertiert. Aber zu was? Ein Zeiger auf sein erstes Element. Der Elementtyp ist nicht ein Zeiger, sondern ein Array. Als Folge geben Sie einen Zeiger auf ein Array von 10 int:

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

Es ist weder ein Array von int*, noch ein int**. Sie können fragen, warum das Array nicht als int** übergeben wird. Das liegt daran, dass der Compiler die Zeilenlänge kennen muss. Wenn Sie eine array[1][0] machen, adressiert der Compiler eine Stelle sizeof(int) * 10 Bytes vom Anfang des zweidimensionalen Arrays entfernt. Es decodiert diese Informationen im Zeiger-zu-Array-Typ.

Sie müssen also zwischen einem der oben genannten vollwertigen Funktions-Prototypen wählen. Natürlich ist der letzte nur verwirrend. Der Compiler ignoriert nur stillschweigend jede Zahl, die in der äußersten Dimension geschrieben wird, wenn ein Parameter als Array deklariert wird. Also würde ich auch nicht die vorletzte Version verwenden. Am besten ist es, die erste oder zweite Version zu verwenden. Was zu beachten ist, ist, dass C keine (realen) Array-Parameter hat! Der Parameter wird am Ende ein Zeiger sein (Zeiger auf Array in diesem Fall).

Beachten Sie, wie der multidimensionale Fall von oben ähnlich dem entarteten, eindimensionalen Fall unten ist. Alle folgenden vier Versionen völlig gleichwertig sind:

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

Obwohl Mark Pims Antwort vollständig auf das Hauptproblem meiner Frage zentriert ist, möchte ich irgendwie all Ihre Bemühungen kompensieren, die Teilmengen von Arrays als Parameter zu erklären C, indem Sie diese Antwort wie empfohlen einstellen. Vielen Dank! – Auron

1

Sie sollten bar definieren als:

bar(int* matrix) 

In C alle Arrays als int* (oder type_of_element* für andere Typen) übergeben werden sollen.

int ** wäre in Ordnung, wenn Ihre Daten wirklich ein Array von Zeigern wäre. int[*data[] zum Beispiel. Das bekommen Sie in main(int argc, char *argv[]).

6

Das Problem ist, dass die Datenstrukturmatrix [10] [10] tatsächlich keine Tabelle von zehn Zeigern zu Array [10] ist, aber es ist ein sequenzielles Array von 100 ganzen Zahlen. Die richtige Signatur für Bar ist

bar (int matrix[10][10]) 

Wenn Sie tatsächlich die Matrix mit indirection darstellen wollen und int haben ** Matrix als Parametertyp für Bar, dann müssen Sie es anders verteilen:

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

Nun entspricht 'Matrix' dem Typ int **. 'Matrix' ist ein Array von zehn Zeigern, und Sie können es mit einem Zeiger übergeben, wodurch Sie den zweiten * erhalten.

+0

„Das Problem Werte zuzugreifen, ist dass die Datenstrukturmatrix [10] [10] tatsächlich keine Tabelle von zehn Zeigern zu Arrays [10] ist, sondern eine sequentielle Matrix von 100 ganzen Zahlen. " Deshalb war ich so verwirrt! – Auron

14

Das Übergeben mehrdimensionaler Arrays in C ist ein heikles Thema. Siehe this FAQ.

Die Frage zu stellen ist, wie Sie bar verwenden werden. Wenn Sie immer wissen, wird es ein 10x10-Array übergeben werden dann umschreiben es als

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

Wenn Sie mit unterschiedlichen Abmessungen mit Arrays bewältigen wollen, dann können Sie in den Längen passieren müssen:

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

Ihre ist eine gute Antwort. Vielen Dank! – Auron

-1
int **matrix 

würde angeben, dass Sie einen Zeiger auf einen Zeiger auf int haben. Dies wird üblicherweise verwendet, um einen Zeiger auf ein Array von Zeigern (auch als Vektor bezeichnet) anzuzeigen. Das ist definitiv nicht der Fall mit

int matrix[10][10] 

, die einen eher ein Zeiger auf einen einzigen Abschnitt des Speichers für 10x10 Ints bemessen ist. Versuchen Sie, zu:

void bar(int *matrix[]) 
+0

bar wertet int-Matrix aus und ist nicht so, wie Sie die Matrix übergeben würden – Ulterior

2

Hier einige Code ist zu üben - es alle möglichen Arten von geben 2dimensional Array und Code enthält Element

#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; 
}