wake-up-neo.com

Wie werden mehrdimensionale Arrays im Speicher formatiert?

In C weiß ich, dass ich mit dem folgenden Code ein zweidimensionales Array auf dem Heap dynamisch zuweisen kann:

int** someNumbers = malloc(arrayRows*sizeof(int*));

for (i = 0; i < arrayRows; i++) {
    someNumbers[i] = malloc(arrayColumns*sizeof(int));
}

Dies erzeugt eindeutig ein eindimensionales Array von Zeigern auf eine Reihe separater eindimensionaler Arrays von ganzen Zahlen, und "The System" kann herausfinden, was ich meine, wenn ich frage:

someNumbers[4][2];

Aber wenn ich statisch ein 2D-Array deklariere, wie in der folgenden Zeile ...:

int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];

... wird eine ähnliche Struktur auf dem Stack erstellt oder hat sie eine andere Form? (d. h. handelt es sich um ein 1D-Array von Zeigern? Wenn nicht, was ist das und wie werden Verweise darauf ermittelt?)

Und als ich "Das System" sagte, was ist eigentlich dafür verantwortlich, das herauszufinden? Der Kernel? Oder erledigt der C-Compiler das beim Kompilieren?

159
Chris Cooper

Ein statisches zweidimensionales Array sieht aus wie ein Array von Arrays - es ist nur zusammenhängend im Speicher angeordnet. Arrays sind nicht dasselbe wie Zeiger, aber da Sie sie oft austauschbar verwenden können, kann es manchmal verwirrend werden. Der Compiler behält jedoch den Überblick, wodurch alles gut ausgerichtet ist. Sie müssen vorsichtig mit statischen 2D-Arrays sein, wie Sie es bereits erwähnt haben, da Sie versuchen, eines an eine Funktion zu übergeben, die ein int ** Parameter, es werden schlimme Dinge passieren. Hier ist ein kurzes Beispiel:

int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}};

In Erinnerung sieht das so aus:

0 1 2 3 4 5

gena das gleiche wie:

int array2[6] = { 0, 1, 2, 3, 4, 5 };

Aber wenn Sie versuchen, array1 zu dieser Funktion:

void function1(int **a);

sie erhalten eine Warnung (und die App greift nicht richtig auf das Array zu):

warning: passing argument 1 of ‘function1’ from incompatible pointer type

Weil ein 2D-Array nicht dasselbe ist wie int **. Das automatische Zerfallen eines Arrays in einen Zeiger geht sozusagen nur "eine Ebene tief". Sie müssen die Funktion wie folgt deklarieren:

void function2(int a[][2]);

oder

void function2(int a[3][2]);

Um alles glücklich zu machen.

Das gleiche Konzept gilt auch für n - dimensionale Arrays. Das Ausnutzen dieser Art von witzigem Geschäft in Ihrer Anwendung erschwert jedoch im Allgemeinen das Verständnis. Also sei vorsichtig da draußen.

125
Carl Norum

Die Antwort basiert auf der Idee, dass C nicht wirklich haben 2D-Arrays - es hat Arrays-of-Arrays. Wenn Sie dies erklären:

int someNumbers[4][2];

Sie fordern, dass someNumbers ein Array von 4 Elementen ist, wobei jedes Element dieses Arrays vom Typ int [2] Ist (das selbst ein Array von 2 ints ist).

Der andere Teil des Puzzles ist, dass Arrays immer zusammenhängend im Speicher angeordnet sind. Wenn Sie fragen nach:

sometype_t array[4];

dann sieht das immer so aus:

| sometype_t | sometype_t | sometype_t | sometype_t |

(4 sometype_t Objekte ohne Leerzeichen nebeneinander). In Ihrem someNumbers Array-of-Arrays sieht es also so aus:

| int [2]    | int [2]    | int [2]    | int [2]    |

Und jedes int [2] Element ist selbst ein Array, das so aussieht:

| int        | int        |

Insgesamt erhalten Sie Folgendes:

| int | int  | int | int  | int | int  | int | int  |
76
caf
unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}};

im Gedächtnis ist gleich:

unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9};
26
kanghai

Als Antwort auf Ihr auch: Beide, obwohl der Compiler den größten Teil der Arbeit leistet.

Bei statisch zugewiesenen Arrays ist "The System" der Compiler. Es reserviert den Speicher wie für jede Stack-Variable.

Im Fall des malloc-Arrays ist "The System" der Implementierer von malloc (normalerweise der Kernel). Der Compiler weist nur den Basiszeiger zu.

Der Compiler wird den Typ immer so behandeln, wie er deklariert wurde, außer in dem von Carl angegebenen Beispiel, in dem er die austauschbare Verwendung herausfinden kann. Aus diesem Grund muss bei der Übergabe eines [] [] an eine Funktion davon ausgegangen werden, dass es sich um eine statisch zugewiesene Ebene handelt, wobei ** als Zeiger auf Zeiger angenommen wird.

5
Jon L

Berücksichtigen Sie für den Zugriff auf ein bestimmtes 2D-Array die Speicherzuordnung für eine Array-Deklaration, wie im folgenden Code gezeigt:

    0  1
a[0]0  1
a[1]2  3

Um auf jedes Element zuzugreifen, reicht es aus, nur das Array, an dem Sie interessiert sind, als Parameter an die Funktion zu übergeben. Verwenden Sie dann offset für column, um auf jedes Element einzeln zuzugreifen.

int a[2][2] ={{0,1},{2,3}};

void f1(int *ptr);

void f1(int *ptr)
{
    int a=0;
    int b=0;
    a=ptr[0];
    b=ptr[1];
    printf("%d\n",a);
    printf("%d\n",b);
}

int main()
{
   f1(a[0]);
   f1(a[1]);
    return 0;
}
1