Eine Struktur mit einem flexiblen Array-Mitglied soll anscheinend nicht deklariert, sondern in Verbindung mit einem Zeiger auf diese Struktur verwendet werden. Beim Deklarieren eines flexiblen Array-Mitglieds muss mindestens ein weiteres Mitglied vorhanden sein, und das flexible Array-Mitglied muss das letzte Mitglied in dieser Struktur sein.
Nehmen wir an, ich habe eine, die so aussieht:
struct example{
int n;
int flm[];
}
Um es dann zu verwenden, muss ich einen Zeiger deklarieren und malloc verwenden, um Speicher für den Inhalt der Struktur zu reservieren.
struct example *ptr = malloc(sizeof(struct example) + 5*sizeof(int));
Das heißt, wenn ich meinen Film will[] Array für fünf ganze Zahlen. Dann kann ich meine Struktur einfach so verwenden:
ptr->flm[0] = 1;
Meine Frage ist, sollte ich nicht stattdessen einfach einen Zeiger verwenden können? Es wäre nicht nur vor C99 kompatibel, sondern ich könnte es mit oder ohne Zeiger auf diese Struktur verwenden. In Anbetracht dessen, dass ich bereits malloc mit dem Film verwenden muss, sollte ich dies nicht einfach tun können?
Betrachten Sie diese neue Definition der Beispielstruktur;
struct example{
int n;
int *notflm;
}
struct example test = {4, malloc(sizeof(int) * 5)};
Ich könnte den Ersatz sogar auf die gleiche Weise wie das flexible Array-Mitglied verwenden:
Würde das auch funktionieren? (Vorausgesetzt obige Beispieldefinition mit notflm)
struct example test;
test.n = 4;
notflm = malloc(sizeof(int) * 5);
Zeiger sind keine Arrays. Die grundlegenden Gründe für die Wahl der zu verwendenden Methode sind die gleichen wie immer bei Arrays im Vergleich zu Zeigern. Im speziellen Fall von flexiblen Array-Mitgliedern gibt es hier einige Gründe, warum Sie sie einem Zeiger vorziehen könnten:
-
Reduzierung der Speicheranforderungen. Ein Zeiger vergrößert Ihre Struktur um (normalerweise) 4 oder 8 Bytes, und Sie werden viel mehr Overhead ausgeben, wenn Sie den Speicher, auf den verwiesen wird, separat statt mit einem einzigen Aufruf zuweisen malloc
.
-
Verbesserung der Zugriffseffizienz. Ein flexibles Anordnungselement befindet sich in einem konstanten Versatz von der Strukturbasis. Ein Zeiger erfordert eine separate Dereferenzierung. Dies wirkt sich sowohl auf die Anzahl der für den Zugriff erforderlichen Anweisungen als auch auf den Druck aus.
-
Atomarität von Zuweisungserfolg/-misserfolg. Wenn Sie die Struktur und den Speicher, auf den sie zeigen soll, als zwei separate Schritte zuweisen, wird Ihr Code zum Aufräumen in den Fehlerfällen viel hässlicher, da Sie den Fall haben, in dem einer erfolgreich war und der andere fehlschlug. Dies kann mit etwas Zeigerarithmetik vermieden werden, um beide aus demselben herauszuschneiden malloc
Anfrage, aber es ist leicht, die Logik falsch zu verstehen und UB aufgrund von Ausrichtungsproblemen aufzurufen.
-
Vermeidung von Deep-Copy. Wenn Sie anstelle eines Zeigers ein flexibles Array verwenden, können Sie einfach memcpy (nicht zuweisen, da die Zuweisung die Länge des flexiblen Arrays nicht kennen kann) kopieren, um die Struktur zu kopieren, anstatt auch die Daten kopieren zu müssen, auf die gezeigt wird, und den Zeiger zu reparieren im neuen Exemplar.
-
Vermeiden der Notwendigkeit für deep-free. Es ist sehr bequem und sauber, nur zu können free
ein einzelnes Objekt, anstatt es zu müssen free
auch Daten, auf die verwiesen wird. Dies kann auch mit dem „Schneiden eines Singles“ erreicht werden malloc
“-Ansatz natürlich, aber flexible Arrays machen es einfacher und weniger fehleranfällig.
-
Sicherlich noch viele weitere Gründe…
Diese Konzepte sind definitiv nicht notwendig, wie Sie selbst betont haben.
Die Unterschiede zwischen den beiden, die Sie demonstriert haben, liegen darin, wo sich Ihre Daten im Speicher befinden.
Im ersten Beispiel mit flexiblem Array befinden sich Ihre Metadaten und das Array selbst im selben Speicherblock und können bei Bedarf als ein Block (Zeiger) verschoben werden.
Im zweiten Beispiel befinden sich Ihre Metadaten auf dem Stack und Ihr Array an anderer Stelle auf dem Heap. Um es zu verschieben/kopieren, müssen Sie nun zwei Speicherblöcke verschieben und den Zeiger in Ihrer Metadatenstruktur aktualisieren.
Im Allgemeinen werden Arrays mit flexibler Größe verwendet, wenn Sie ein Array und seine Metadaten räumlich zusammen im Speicher platzieren müssen.
Ein Beispiel, bei dem dies definitiv nützlich ist, ist beispielsweise das Platzieren eines Arrays mit seinen Metadaten in einer Datei – Sie haben nur einen zusammenhängenden Speicherblock und jedes Mal, wenn Sie ihn laden, wird er (höchstwahrscheinlich) an einem anderen Ort Ihrer VM platziert .
Mögliches Duplikat von Flexible Array-Mitglieder in C – schlecht?. Flexible Arrays sind Teil von C99; Zeiger sind nicht und können Portabilitätsprobleme haben.
–Ted Hopp
1. August 2013 um 18:28 Uhr
Der Hauptunterschied besteht darin, dass flexible Array-Mitglieder es Ihnen ermöglichen, die gesamte Struktur in einem zusammenhängenden Block zuzuweisen. Wenn Sie die Struktur kopieren möchten, berechnen Sie einfach ihre Größe und erstellen Sie eine Kopie. Während Sie bei einem Zeiger die Struktur selbst kopieren müssen, kopieren Sie dann auch das innere Array.
– Drew McGowen
1. August 2013 um 18:31 Uhr
@TedHopp Auf welche denkbare Weise könnten Sie möglicherweise ernsthaft sagen, dass Zeiger nicht Teil von C99 sind?
– Das ist nicht mein richtiger Name
5. August 2013 um 17:55 Uhr
@ElchononEdelson – Das war schlecht formuliert und sollte in einem sehr engen Kontext interpretiert werden. Ich meinte, dass die Verwendung von Zeigern am Ende von a
struct
als Methode zur Implementierung flexibler Arrays ist nicht Teil des C99-Standards (und kann Portabilitätsprobleme haben, hauptsächlich aufgrund versteckter Polsterung für die Ausrichtung von Arrays/Zeigern).–Ted Hopp
5. August 2013 um 18:07 Uhr
Reden wir hier von verschiedenen Dingen? Um einen Zeiger am Ende einer Struktur als flexibles Array-Mitglied zu verwenden, müssten Sie haben
struct foo {int bar; int *ary;} *my; my=malloc(sizeof(struct foo)+10*sizeof(int)); my.ary=(int*)(my+1);
, etwa. Das ist natürlich zutiefst dumm. Die andere Sache ist, das letzte Element der Struktur einen Zeiger auf eine andere Stelle sein zu lassen, die ordnungsgemäß mit der Adresse eines ordnungsgemäß zugewiesenen Speicherblocks initialisiert wurde. Dies stellt keine Struktur mit einem flexiblen Array-Mitglied dar, es ist einfach eine andere Technik mit eigenen unterschiedlichen Problemen.– Das ist nicht mein richtiger Name
5. August 2013 um 18:56 Uhr