Übergeben von Arrays und Matrizen an Funktionen als Zeiger und Zeiger auf Zeiger in C

Lesezeit: 5 Minuten

Angesichts des folgenden Codes:

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 bekomme:

Warnung: Übergeben von Argument 1 von „bar“ von einem inkompatiblen Zeigertyp

Obwohl der Aufruf von ‘foo’ in Ordnung zu sein scheint.

Vielen Dank 🙂

Benutzer-Avatar
Johannes Schaub – litb

Nun, es wird von der C-Community sicherlich nicht gut verstanden, wie ein Blick auf SO zeigt. Die Magie ist, alle folgenden sind zu 100 % gleichwertig:

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, den Unterschied zwischen einem Zeiger und einem Array zu machen. Ein Array ist kein Zeiger. Ein Array kann in einen Zeiger auf sein erstes Element umgewandelt werden. Wenn Sie einen Zeiger haben, haben Sie diesen:

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

Wenn Sie jedoch ein Array haben, haben Sie Folgendes:

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

Mit dem Zeiger befinden sich die Daten auf einem ganz anderen Planeten, sind aber mit dem Zeiger verknüpft. Ein Array enthält die Daten selbst. Jetzt, Ein mehrdimensionales Array ist nur ein Array von Arrays. Die Arrays sind in einem übergeordneten Array verschachtelt. Die Größe Ihres Arrays ist also:

(sizeof(int) * 10) * 10

Das liegt daran, dass Sie 10 Arrays haben, die alle Arrays mit 10 Ganzzahlen sind. Wenn Sie dieses Array nun übergeben möchten, wird es konvertiert. Aber wozu? Ein Zeiger auf sein erstes Element. Der Elementtyp ist nicht ein Zeiger, sondern ein Array. Als Konsequenz übergeben 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 fragen sich vielleicht, warum das Array nicht als übergeben wird int**. Das liegt daran, dass der Compiler die Zeilenlänge kennen muss. Wenn Sie eine array[1][0]adressiert der Compiler einen Ort sizeof(int) * 10 Bytes abgesehen vom Beginn des zweidimensionalen Arrays. Es dekodiert diese Informationen im Pointer-to-Array-Typ.

Sie müssen sich also für einen der oben genannten voll gleichwertigen Funktionsprototypen entscheiden. Letzteres ist natürlich nur verwirrend. Der Compiler ignoriert einfach stillschweigend jede Zahl, die in die äußerste Dimension geschrieben wird, wenn ein Parameter als Array deklariert ist. Daher würde ich die vorletzte Version auch nicht verwenden. Verwenden Sie am besten die erste oder zweite Version. Es ist wichtig, sich daran zu erinnern C hat keine (echten) Array-Parameter! Der Parameter ist am Ende ein Zeiger (in diesem Fall ein Zeiger auf ein Array).

Beachten Sie, wie der mehrdimensionale Fall oben dem degenerierten eindimensionalen Fall unten ähnlich ist. Alle der folgenden 4 Versionen sind vollständig gleichwertig:

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

  • Obwohl sich die Antwort von Mark Pim vollständig auf das Hauptproblem meiner Frage konzentriert, möchte ich irgendwie all Ihre Bemühungen kompensieren, die versuchen, die Subtelies von Arrays als Parameter in C zu erklären, indem ich diese Antwort wie empfohlen einstelle. Vielen Dank!

    – Auron

    14. Februar 2009 um 0:39 Uhr

Benutzer-Avatar
Markus Pim

Das Übergeben mehrdimensionaler Arrays in C ist ein kniffliges Thema. Sehen diese häufig gestellten Fragen.

Die zu stellende Frage ist, wie Sie es verwenden werden bar. Wenn Sie immer wissen, dass ein 10×10-Array übergeben wird, schreiben Sie es neu als

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

Wenn Sie mit Arrays unterschiedlicher Dimensionen umgehen möchten, müssen Sie möglicherweise die Längen übergeben:

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

  • Ihre Antwort ist großartig. Vielen Dank!

    – Auron

    14. Februar 2009 um 0:40 Uhr

Das Problem ist, dass die Datenstruktur matrix[10][10] ist eigentlich keine Tabelle mit zehn Zeigern auf ein Array[10]’s, aber es ist ein sequentielles Array von 100 ganzen Zahlen. Die richtige Signatur für bar ist

bar (int matrix[10][10])

Wenn Sie die Matrix tatsächlich über den Umweg darstellen wollen und int **matrix als Parametertyp für bar haben, dann müssen Sie sie anders belegen:

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

Jetzt passt ‘matrix’ zum Typ int **. ‘matrix’ ist ein Array von zehn Zeigern, und Sie können es als Zeiger übergeben, wodurch Sie das zweite * erhalten.

  • „Das Problem ist, dass die Datenstruktur Matrix[10][10] ist eigentlich keine Tabelle mit zehn Zeigern auf ein Array[10]’s, aber es ist ein sequentielles Array von 100 ganzen Zahlen.” Deshalb war ich so verwirrt!

    – Auron

    13. Februar 2009 um 18:54 Uhr

Hier ist etwas Code zum Üben – er enthält alle möglichen Arten der Übergabe von zweidimensionalen Arrays und Code für den Zugriff auf Elementwerte

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

Sie sollten bar wie folgt definieren:

bar( int* matrix )

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

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

Benutzer-Avatar
dwc

int **matrix

würde anzeigen, dass Sie einen Zeiger auf einen Zeiger auf int haben. Dies wird häufig verwendet, um einen Zeiger auf ein Array von Zeigern (auch Vektor genannt) anzugeben. Das ist definitiv NICHT der Fall mit

int matrix[10][10]

Dies ist eher ein Zeiger auf einen einzelnen Speicherabschnitt mit einer Größe von 10 x 10 Ints. Versuchen Sie, Folgendes zu ändern:

void bar(int *matrix[])

1093440cookie-checkÜbergeben von Arrays und Matrizen an Funktionen als Zeiger und Zeiger auf Zeiger in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy