Diese Redewendung fällt natürlich aus der 1D-Array-Zuordnung heraus. Beginnen wir mit der Zuweisung eines 1D-Arrays eines beliebigen Typs T
:
T *p = malloc( sizeof *p * N );
Einfach, richtig? Der Ausdruck *p
hat den Typ T
, sizeof *p
ergibt also das gleiche Ergebnis wie sizeof (T)
, sodass wir genügend Speicherplatz für ein N
Element-Array von zuweisen T
. Dies gilt für jeden TypT
.
Ersetzen wir nun T
durch einen Array-Typ wie R [10]
. Dann wird unsere Zuordnung
R (*p)[10] = malloc( sizeof *p * N);
Die Semantik hier entspricht genau der 1D-Zuordnungsmethode. Alles, was sich geändert hat, ist die Art von p
. Stattdessen T *
ist es jetzt R (*)[10]
. Der Ausdruck *p
hat einen Typ, T
der Typ ist R [10]
, also sizeof *p
äquivalent zu sizeof (T)
dem, der äquivalent zu ist sizeof (R [10])
. Wir weisen also genügend Speicherplatz für ein N
By- 10
Element-Array von zu R
.
Wir können das noch weiter gehen, wenn wir wollen; Angenommen, es R
ist selbst ein Array-Typ int [5]
. Ersetzen Sie das R
und wir bekommen
int (*p)[10][5] = malloc( sizeof *p * N);
Das gleiche Geschäft - sizeof *p
ist das gleiche wie sizeof (int [10][5])
, und wir werden am Ende einen zusammenhängenden Speicherblock zuweisen, der groß genug ist, um ein N
By- 10
by- 5
Array von zu halten int
.
Das ist also die Zuordnungsseite; Was ist mit der Zugangsseite?
Denken Sie daran , dass der []
tiefgestellte Operation definiert in Bezug auf die Zeigerarithmetik: a[i]
ist definiert als *(a + i)
1 . Somit dereferenziert der Indexoperator []
implizit einen Zeiger. Wenn p
es sich um einen Zeiger auf handelt T
, können Sie auf den Wert zugreifen, auf den verwiesen wird, indem Sie explizit mit dem unären *
Operator dereferenzieren :
T x = *p;
oder mithilfe des []
Indexoperators:
T x = p[0]; // identical to *p
Wenn Sie also p
auf das erste Element eines Arrays zeigen , können Sie mit einem Index auf den Zeiger auf jedes Element dieses Arrays zugreifen p
:
T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p
Lassen Sie uns nun unsere Ersetzungsoperation erneut ausführen und durch T
den Array-Typ ersetzen R [10]
:
R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];
Ein sofort offensichtlicher Unterschied; Wir dereferenzieren explizit, p
bevor wir den Indexoperator anwenden. Wir wollen nicht abonnieren p
, wir wollen abonnieren, auf welche p
Punkte (in diesem Fall das Array arr[0]
). Da unary *
eine niedrigere Priorität als der Indexoperator hat []
, müssen wir Klammern verwenden, um explizit p
mit zu gruppieren *
. Aber denken Sie von oben daran, dass dies *p
dasselbe ist wie p[0]
, also können wir das durch ersetzen
R x = (p[0])[i];
oder nur
R x = p[0][i];
Wenn p
wir also auf ein 2D-Array zeigen, können wir wie folgt in dieses Array indizieren p
:
R x = p[i][j]; // access the i'th element of arr through pointer p;
// each arr[i] is a 10-element array of R
Nehmen Sie dies zu dem gleichen Schluss wie oben und ersetzen Sie es R
durch int [5]
:
int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];
Dies funktioniert genauso , wenn p
Punkte auf eine regelmäßige Anordnung, oder wenn es um Speicherpunkte zugewiesen durch malloc
.
Diese Redewendung hat folgende Vorteile:
- Es ist einfach - nur eine Codezeile im Gegensatz zur stückweisen Zuweisungsmethode
T **arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( size_t i = 0; i < N; i++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
}
}
- Alle Zeilen des zugewiesenen Arrays sind * zusammenhängend *, was bei der oben beschriebenen schrittweisen Zuweisungsmethode nicht der Fall ist.
- Die Freigabe des Arrays ist mit einem einzigen Aufruf von genauso einfach
free
. Auch dies gilt nicht für die stückweise Zuweisungsmethode, bei der Sie die Zuordnung aufheben müssen, arr[i]
bevor Sie die Zuordnung aufheben können arr
.
Manchmal ist die stückweise Zuweisungsmethode vorzuziehen, z. B. wenn Ihr Heap stark fragmentiert ist und Sie Ihren Speicher nicht als zusammenhängenden Block zuordnen können, oder wenn Sie ein "gezacktes" Array zuweisen möchten, bei dem jede Zeile eine andere Länge haben kann. Aber im Allgemeinen ist dies der bessere Weg.
1. Denken Sie daran, dass Arrays keine Zeiger sind. Stattdessen werden Array- Ausdrücke nach Bedarf in Zeigerausdrücke konvertiert.