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) verdoppelt.
Angeblich , sage ich, weil mir bisher niemand, den ich gefragt habe, genau sagen konnte, was dies bewirkt, woher es stammt oder warum es funktionieren sollte (was angeblich der Fall ist, aber ich kaufe es noch nicht).
Vielleicht fehlt mir 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 tatsächlich verstehen.
c
arrays
multidimensional-array
malloc
allocation
User1291
quelle
quelle
n+1
sonderndouble (*e)[rows] = malloc(columns * sizeof *e);
Antworten:
Die Variable
e
ist ein Zeiger auf ein Array vonn + 1
Elementen vom Typdouble
.Wenn Sie den Dereferenzierungsoperator aktivieren, erhalten
e
Sie den Basistype
"Array vonn + 1
Elementen vom Typdouble
".Der
malloc
Aufruf nimmt einfach den Basistype
(siehe oben) und erhält seine Größe, multipliziert ihn mitn + 1
und übergibt diese Größe an diemalloc
Funktion. Zuweisen eines Arrays vonn + 1
Arrays vonn + 1
Elementen vondouble
.quelle
sizeof(*e)
entsprichtsizeof(double [n + 1])
. Multiplizieren Sie das mitn + 1
und Sie bekommen genug.n+1
das Ergebnis quadratisch ist , wenn Sie beide Dimensionen eingeben . Wenn Sie dies tundouble (*e)[cols] = malloc(rows * sizeof(*e));
, hat das Ergebnis die von Ihnen angegebene Anzahl von Zeilen und Spalten.int rows = n+1
undint cols = n+1
. Gott rette uns vor klugem Code.Dies ist die typische Methode, mit der Sie 2D-Arrays dynamisch zuweisen sollten.
e
ist ein Array-Zeiger auf ein Array vom Typdouble [n+1]
.sizeof(*e)
gibt daher den Typ des spitzen Typs an, der die Größe einesdouble [n+1]
Arrays hat.n+1
solchen Arrays Platz zu.e
, dass er auf das erste Array in diesem Array von Arrays zeigt.e
als füre[i][j]
den Zugriff einzelner Elemente in der 2D - Array.Persönlich denke ich, dass dieser Stil viel einfacher zu lesen ist:
quelle
ptr = malloc(sizeof *ptr * count)
Stil.malloc(row*col*sizeof(double))
tritt auf, wenn errow*col*sizeof()
überläuft, abersizeof()*row*col
nicht. (zB row, col areint
)sizeof *e * (n+1)
ist einfacher zu pflegen; Wenn Sie sich jemals dazu entschließen, den Basistyp zu ändern ( z. B. vondouble
bislong double
), müssen Sie nur die Deklaration von änderne
. Sie müssen densizeof
Ausdruck immalloc
Aufruf nicht ändern (was Zeit spart und Sie davor schützt, ihn an einer Stelle zu ändern, an der anderen jedoch nicht).sizeof *e
gibt Ihnen immer die richtige Größe.Diese Redewendung fällt natürlich aus der 1D-Array-Zuordnung heraus. Beginnen wir mit der Zuweisung eines 1D-Arrays eines beliebigen Typs
T
:Einfach, richtig? Der Ausdruck
*p
hat den TypT
,sizeof *p
ergibt also das gleiche Ergebnis wiesizeof (T)
, sodass wir genügend Speicherplatz für einN
Element-Array von zuweisenT
. Dies gilt für jeden TypT
.Ersetzen wir nun
T
durch einen Array-Typ wieR [10]
. Dann wird unsere ZuordnungDie Semantik hier entspricht genau der 1D-Zuordnungsmethode. Alles, was sich geändert hat, ist die Art von
p
. StattdessenT *
ist es jetztR (*)[10]
. Der Ausdruck*p
hat einen Typ,T
der Typ istR [10]
, alsosizeof *p
äquivalent zusizeof (T)
dem, der äquivalent zu istsizeof (R [10])
. Wir weisen also genügend Speicherplatz für einN
By-10
Element-Array von zuR
.Wir können das noch weiter gehen, wenn wir wollen; Angenommen, es
R
ist selbst ein Array-Typint [5]
. Ersetzen Sie dasR
und wir bekommenDas gleiche Geschäft -
sizeof *p
ist das gleiche wiesizeof (int [10][5])
, und wir werden am Ende einen zusammenhängenden Speicherblock zuweisen, der groß genug ist, um einN
By-10
by-5
Array von zu haltenint
.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. Wennp
es sich um einen Zeiger auf handeltT
, können Sie auf den Wert zugreifen, auf den verwiesen wird, indem Sie explizit mit dem unären*
Operator dereferenzieren :oder mithilfe des
[]
Indexoperators: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 zugreifenp
:Lassen Sie uns nun unsere Ersetzungsoperation erneut ausführen und durch
T
den Array-Typ ersetzenR [10]
:Ein sofort offensichtlicher Unterschied; Wir dereferenzieren explizit,
p
bevor wir den Indexoperator anwenden. Wir wollen nicht abonnierenp
, wir wollen abonnieren, auf welchep
Punkte (in diesem Fall das Arrayarr[0]
). Da unary*
eine niedrigere Priorität als der Indexoperator hat[]
, müssen wir Klammern verwenden, um explizitp
mit zu gruppieren*
. Aber denken Sie von oben daran, dass dies*p
dasselbe ist wiep[0]
, also können wir das durch ersetzenoder nur
Wenn
p
wir also auf ein 2D-Array zeigen, können wir wie folgt in dieses Array indizierenp
:Nehmen Sie dies zu dem gleichen Schluss wie oben und ersetzen Sie es
R
durchint [5]
:Dies funktioniert genauso , wenn
p
Punkte auf eine regelmäßige Anordnung, oder wenn es um Speicherpunkte zugewiesen durchmalloc
.Diese Redewendung hat folgende Vorteile:
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önnenarr
.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.
quelle