Kann mir jemand erklären, warum technisch ein 2D-Array nicht tatsächlich in int**
abklingt, während Sie bei einem eindimensionalen Array den Pointer-Zerfall auf int*
bekommen (wenn Sie ein Array von int
hätten).
Ich verstehe, was Pointer-Zerfall ist, aber ich verstehe nicht, warum das 2D-Array diesen Zerfall nicht zeigt?
Es ist eine häufige Verwirrung mit Leuten, die neu in C oder C ++ sind, aber ich stolpere auch oft darüber.
Sie haben unterschiedliche Speicherlayouts. Ein 2D-Array ist ein zusammenhängendes Stück Speicher, während int**
ein Array von Zeigern ist. Bei einem 2D-Array wird der Offset zu einer Position als rownum * collength + colnum
berechnet (oder umgekehrt, je nachdem, wie Sie Zeilen / Spalten beschriften). Das würde mit int**
nicht funktionieren, was tatsächlich zwei Speicherlesevorgänge erzeugt; der erste, um den Spaltenzeiger zu bekommen, dann das Lesen der Daten bei einem Offset von diesem Speicher.
Dieses andere Layout ist übrigens der Grund, warum Sie die Array-Dimensionen (alle außer der linken) in Funktionen deklarieren müssen, die 2D-Arrays akzeptieren; andernfalls wäre es dem Compiler nicht möglich, den Code zur Berechnung der Offsets zu generieren.
Es folgt ein Versuch, ein Bild des Speicherlayouts von int**
zu erstellen. Die linke Spalte ist ein zusammenhängendes Array von Zeigern, wobei jeder die Adresse eines zusammenhängenden Speicherstücks mit den Daten enthält. Beachten Sie, dass die Daten in jeder Spalte nicht gleich lang sein müssen (obwohl das für die Lesbarkeit des Codes nicht unbedingt von Vorteil ist):
Gibt etwas wie folgt aus:
%Vor% Im Gegensatz dazu könnte das Layout für int[3][4]
aussehen. Die Klammern zeigen nur den logischen Bruch jeder Datenspalte an. Die Ganzzahlwerte sind zusammenhängend im Speicher:
Gibt etwas wie folgt aus:
%Vor% Das Muster ist, dass ein Ausdruck vom Typ "N-Element-Array von T
" in einen Ausdruck vom Typ "Zeiger auf T
" konvertiert wird. Wenn T
ein Array-Typ ist, werden Sie mit einem Zeiger auf ein Array beendet. Gegeben eine Erklärung wie
Der Ausdruck arr
hat den Typ "M-Element-Array von N-Element-Arrays von T
". Außer wenn es der Operand der Operatoren sizeof
, _Alignof
oder unary &
ist, wird es in einen Ausdruck vom Typ "Zeiger auf N-Element-Array von T
" oder T (*)[N]
konvertiert.
Schreiben Sie im Zweifelsfall den Typ in Englisch aus, z. B. "Vierelementiges Array von Arrays mit fünf Elementen aus Arrays mit sechs Elementen von int
" und ersetzen Sie dann das erste "Array mit n Elementen" durch " Zeiger auf ", geben" Zeiger auf 5-Element-Arrays von 6-Element-Arrays von int
":
Das Ergebnis der Umwandlung eines zweidimensionalen Arrays in einen Zeiger auf sein erstes Element ist ein Zeiger, kein Array. Da es kein Array ist, wird es nicht weiter konvertiert.
Es klingt ab. Ein Ausdruck, dessen Typ "Array von T" ist, zerfällt in einen Zeiger auf sein erstes Element. Für ein zweidimensionales Array von T
hat das erste Element den Typ Array von T
. Also:
In den meisten Kontexten hat arr
den Typ "Zeiger auf int
". Wenden Sie nun die selbe -Regel auf ein zweidimensionales Array an:
Hier hat arr2
den Typ "pointer to array of 5 int
". Beachten Sie, dass der Typ dieses Objekts nicht "Array von T
" ist, sodass es nicht weiter zerfällt.
Ein zweidimensionales Array ist ein Array von Arrays .
Ein "Array von T
" wird - in den meisten Kontexten - in einen "Zeiger auf T
" konvertiert, also wird ein "Array von T[N]
" in einen "Zeiger auf T[N]
" konvertiert.
Seit der Speicherdarstellung eines
%Vor%unterscheidet sich vollständig von
%Vor% Ein int[M][N]
kann nicht einmal in ein int**
konvertiert werden.
Es ist ein häufiger Fehler, dass man denken könnte, dass wenn int* i
== int i[5]
als int** i
== int i[5][5]
, aber es ist nicht so, dass aus zwei Gründen. Erstens ist ein int**
ein Zeiger auf einen Zeiger und hat nichts mit den Wegen zu tun Arrays sind ausgelegt und zweitens ist ein 2D Array tatsächlich ein 1D Array intern und was es tut, ist das zweite Array nebeneinander zu legen.
Das bedeutet, dass in einem Array von [2][2]
intern ein Array von [4]
erstellt wird. Wenn Sie versuchen, auf [0][0]
zuzugreifen, wird dieser in [0]
übersetzt, [0][1]
wird in [1]
übersetzt. , [1][0]
wird in [2]
übersetzt und [1][1]
wird in [3]
Beispiel:
%Vor% Dies wird 125 drucken, weil ich [2][2]
auf 125 setze und das wird als gleich 12
angesehen, da die erste [0]
von 0 - 4 besetzt wird, [1]
wird von 5-10 und so besetzt weiter und so weiter.
Ein 2D-Array ist ein zusammenhängender Speicherblock. Wenn Sie darauf als 2D-Array zugreifen, benötigen Sie die Zeilenlänge, um von Spalte zu Spalte zu springen. Ein Zeiger auf einen Zeiger ist genau das. Es kann ein 2D-Array simulieren, wenn der erste Zeiger ein Array von Zeigern zu einer zweiten Dimension von Arrays ist, aber es ist ein ganz anderes Biest als der zusammenhängende Speicherblock, der ein 2D-Array ist.