In einem Projekt hat jemand diese Zeile verschoben:
double (*e)[n+1] = malloc((n+1) * sizeof(*e));
Was angeblich ein zweidimensionales Array von (n + 1) * (n + 1) Doubles erzeugt.
Angeblichsage ich, weil mir bisher niemand, den ich gefragt habe, sagen konnte, was das genau macht, woher es kommt oder warum es funktionieren sollte (was es angeblich tut, aber ich kaufe es noch nicht).
Vielleicht übersehe ich etwas Offensichtliches, aber ich würde es begrüßen, wenn mir jemand die obige Zeile erklären könnte. Denn ich persönlich würde mich viel besser fühlen, wenn wir etwas verwenden würden, das wir wirklich verstehen.
Die Variable e
ist ein Zeiger auf ein Array von n + 1
Elemente des Typs double
.
Verwenden des Dereferenzierungsoperators on e
gibt Ihnen den Basistyp von e
das ist “Array von n + 1
Elemente des Typs double
“.
Das malloc
Aufruf nimmt einfach den Basistyp von e
(oben erklärt) und erhält seine Größe, multipliziert sie mit n + 1
und diese Größe an die übergeben malloc
Funktion. Im Wesentlichen Zuweisung eines Arrays von n + 1
Arrays von n + 1
Elemente von double
.
Dies ist die typische Art und Weise, wie Sie 2D-Arrays dynamisch zuweisen sollten.
e
ist ein Array-Zeiger auf ein Array vom Typ double [n+1]
.
sizeof(*e)
gibt daher den Typ des spitz zulaufenden Typs an, der die Größe von eins hat double [n+1]
Reihe.
- Sie weisen Raum zu
n+1
solche Arrays.
- Sie setzen den Array-Zeiger
e
um auf das erste Array in diesem Array von Arrays zu zeigen.
- Dies ermöglicht Ihnen die Verwendung
e
wie e[i][j]
um auf einzelne Elemente im 2D-Array zuzugreifen.
Ich persönlich denke, dass dieser Stil viel einfacher zu lesen ist:
double (*e)[n+1] = malloc( sizeof(double[n+1][n+1]) );
Dieses Idiom fällt natürlich aus der 1D-Array-Zuweisung. Beginnen wir mit der Zuweisung eines 1D-Arrays eines beliebigen Typs T
:
T *p = malloc( sizeof *p * N );
Einfach, oder? Das Ausdruck *p
Typ hat T
Also sizeof *p
liefert das gleiche Ergebnis wie sizeof (T)
also weisen wir genügend Platz für eine zu N
-Elementarray von T
. Dies gilt für jeder Typ T
.
Lassen Sie uns nun ersetzen T
mit einem Array-Typ wie R [10]
. Dann wird unsere Zuordnung
R (*p)[10] = malloc( sizeof *p * N);
Die Semantik hier ist genauso als 1D-Zuordnungsverfahren; alles, was sich geändert hat, ist die Art von p
. Anstatt von T *
es schneit R (*)[10]
. Der Ausdruck *p
Typ hat T
was Typ ist R [10]
Also sizeof *p
ist äquivalent zu sizeof (T)
was äquivalent ist sizeof (R [10])
. Also weisen wir genug Platz für eine zu N
durch 10
Elementarray von R
.
Wir können das sogar noch weiter treiben, wenn wir wollen; vermuten R
ist selbst ein Array-Typ int [5]
. Ersetze das durch R
und wir bekommen
int (*p)[10][5] = malloc( sizeof *p * N);
Gleicher Deal – sizeof *p
ist das gleiche wie sizeof (int [10][5])
und am Ende weisen wir einen zusammenhängenden Teil des Speichers zu, der groß genug ist, um a aufzunehmen N
durch 10
durch 5
Anordnung von int
.
Das ist also die Allokationsseite; Was ist mit der Zugangsseite?
Denken Sie daran, dass die []
Indexoperation ist definiert in Bezug auf die Zeigerarithmetik: a[i]
ist definiert als *(a + i)
1. Also der Indexoperator []
implizit dereferenziert einen Zeiger. Wenn p
ist ein Hinweis auf T
können Sie auf den Wert, auf den gezeigt wird, zugreifen, indem Sie entweder explizit mit dem unären dereferenzieren *
Operator:
T x = *p;
oder durch die Verwendung der []
Indexoperator:
T x = p[0]; // identical to *p
Also wenn p
zeigt auf das erste Element von an Reihekönnen Sie auf jedes Element dieses Arrays zugreifen, indem Sie einen Index auf dem Zeiger verwenden 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 Substitutionsoperation wiederholen und ersetzen T
mit dem Array-Typ 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 der Indexoperator angewendet wird. Wir wollen nicht subskribieren p
wir wollen was subskribieren p
verweist auf (in diesem Fall die Reihe arr[0]
). Da unär *
hat einen niedrigeren Vorrang als der Index []
-Operator müssen wir Klammern verwenden, um explizit zu gruppieren p
mit *
. Aber denken Sie daran von oben *p
ist das gleiche wie p[0]
also können wir das durch ersetzen
R x = (p[0])[i];
oder nur
R x = p[0][i];
Also wenn p
auf ein 2D-Array zeigt, können wir durch dieses Array indizieren p
so:
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 der gleichen Schlussfolgerung wie oben und ersetzen Sie es R
mit 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];
Das funktioniert nur das Gleiche wenn p
auf ein reguläres Array zeigt oder auf Speicher zeigt, der durch zugewiesen wurde malloc
.
Diese Redewendung hat folgende Vorteile:
- Es ist einfach – nur eine Codezeile, im Gegensatz zur stückweisen Zuordnungsmethode
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 obigen Methode der stückweisen Zuweisung nicht der Fall ist;
- Das Freigeben des Arrays ist mit einem einzigen Aufruf genauso einfach
free
. Auch dies gilt nicht für die stückweise Zuweisungsmethode, bei der Sie jede Zuweisung aufheben müssen arr[i]
bevor Sie die Zuordnung aufheben können arr
.
Manchmal ist die stückweise Zuordnungsmethode vorzuziehen, z. B. wenn Ihr Heap stark fragmentiert ist und Sie Ihren Speicher nicht als zusammenhängenden Block zuweisen können oder wenn Sie ein “gezacktes” Array zuweisen möchten, in dem jede Zeile eine andere Länge haben kann. Aber im Allgemeinen ist dies der bessere Weg.
1. Denken Sie daran, dass Arrays sind nicht Zeiger – stattdessen Array Ausdrücke werden bei Bedarf in Zeigerausdrücke konvertiert.
Fürs Protokoll, das ist die einzige Möglichkeit, ein tatsächliches 2D-Array dynamisch zuzuweisen.
– QUentin
22. April 2016 um 13:06 Uhr