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 *pergibt also das gleiche Ergebnis wie sizeof (T), sodass wir genügend Speicherplatz für ein NElement-Array von zuweisen T. Dies gilt für jeden TypT .
Ersetzen wir nun Tdurch 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 *phat einen Typ, Tder 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 NBy- 10Element-Array von zu R.
Wir können das noch weiter gehen, wenn wir wollen; Angenommen, es Rist selbst ein Array-Typ int [5]. Ersetzen Sie das Rund wir bekommen
int (*p)[10][5] = malloc( sizeof *p * N);
Das gleiche Geschäft - sizeof *pist das gleiche wie sizeof (int [10][5]), und wir werden am Ende einen zusammenhängenden Speicherblock zuweisen, der groß genug ist, um ein NBy- 10by- 5Array 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 pes 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 pauf 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 Tden 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, pbevor 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 pmit zu gruppieren *. Aber denken Sie von oben daran, dass dies *pdasselbe ist wie p[0], also können wir das durch ersetzen
R x = (p[0])[i];
oder nur
R x = p[0][i];
Wenn pwir 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 Rdurch 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 pPunkte 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.