Im Linux-Kernel-Code habe ich Folgendes gefunden, was ich nicht verstehen kann.
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
Der Code ist hier: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
Was ist die Notwendigkeit und der Zweck eines Datenarrays mit null Elementen?
c
structure
flexible-array-member
Jeegar Patel
quelle
quelle
Antworten:
Dies ist eine Möglichkeit, variable Datengrößen zu erhalten, ohne
malloc
(kmalloc
in diesem Fall) zweimal aufrufen zu müssen . Sie würden es so verwenden:Dies war früher kein Standard und wurde als Hack angesehen (wie Aniket sagte), aber es wurde in C99 standardisiert . Das Standardformat dafür ist jetzt:
Beachten Sie, dass Sie keine Größe für das
data
Feld angeben. Beachten Sie auch, dass diese spezielle Variable nur am Ende der Struktur stehen kann.In C99 wird diese Angelegenheit in 6.7.2.1.16 (Schwerpunkt Mine) erläutert:
Oder mit anderen Worten, wenn Sie haben:
Sie können
var->data
mit Indizes in zugreifen[0, extra)
. Beachten Sie, dasssizeof(struct something)
nur die Größe für die anderen Variablen angegeben wird, dhdata
eine Größe von 0.Es kann auch interessant sein festzustellen, wie der Standard tatsächlich Beispiele für ein
malloc
solches Konstrukt enthält (6.7.2.1.17):Ein weiterer interessanter Hinweis des Standards an derselben Stelle ist (Hervorhebung von mir):
quelle
[0, extra)
?Dies ist tatsächlich ein Hack für GCC ( C90 ).
Es wird auch als Struktur-Hack bezeichnet .
Also würde ich das nächste Mal sagen:
Es wird gleichbedeutend sein mit zu sagen:
Und ich kann beliebig viele solcher Strukturobjekte erstellen.
quelle
Die Idee ist, ein Array mit variabler Größe am Ende der Struktur zuzulassen. Vermutlich
bts_action
handelt es sich um ein Datenpaket mit einem Header fester Größe (die Felder undtype
undsize
) und einem Element variabler Größedata
. Durch Deklarieren als Array mit 0 Länge kann es wie jedes andere Array indiziert werden. Sie würden dann einebts_action
Struktur mit einerdata
Größe von beispielsweise 1024 Byte wie folgt zuweisen :Siehe auch: http://c2.com/cgi/wiki?StructHack
quelle
malloc
Sie dazu bringt, sich mehrmals zu wiederholen, und wenn jemals die Art deraction
Änderungen, müssen Sie es mehrmals korrigieren . Vergleichen Sie die folgenden beiden für sich selbst und Sie werden wissen:struct some_thing *variable = (struct some_thing *)malloc(10 * sizeof(struct some_thing));
vs.struct some_thing *variable = malloc(10 * sizeof(*variable));
Die zweite ist kürzer, sauberer und deutlich einfacher zu ändern.Der Code ist ungültig C ( siehe hier ). Der Linux-Kernel befasst sich aus offensichtlichen Gründen nicht im geringsten mit der Portabilität, daher verwendet er viel nicht standardmäßigen Code.
Was sie tun, ist eine nicht standardmäßige GCC-Erweiterung mit Arraygröße 0. Ein standardkonformes Programm hätte geschrieben
u8 data[];
und es hätte genau dasselbe bedeutet. Die Autoren des Linux-Kernels lieben es anscheinend, Dinge unnötig kompliziert und nicht standardisiert zu machen, wenn sich eine Option dazu ergibt.In älteren C-Standards wurde das Beenden einer Struktur mit einem leeren Array als "Struktur-Hack" bezeichnet. Andere haben ihren Zweck bereits in anderen Antworten erläutert. Der Struktur-Hack im C90-Standard war ein undefiniertes Verhalten und konnte zu Abstürzen führen, hauptsächlich da ein C-Compiler am Ende der Struktur eine beliebige Anzahl von Füllbytes hinzufügen kann. Solche Auffüllbytes können mit den Daten kollidieren, die Sie am Ende der Struktur "hacken" wollten.
GCC hat frühzeitig eine nicht standardmäßige Erweiterung vorgenommen, um dies von undefiniertem zu genau definiertem Verhalten zu ändern. Der C99-Standard hat dieses Konzept dann angepasst, und jedes moderne C-Programm kann diese Funktion daher ohne Risiko nutzen. Es ist in C99 / C11 als flexibles Array-Mitglied bekannt .
quelle
Eine andere Verwendung des Arrays mit der Länge Null ist die Verwendung einer benannten Bezeichnung innerhalb einer Struktur, um die Überprüfung des Kompilierungszeit-Strukturversatzes zu unterstützen.
Angenommen, Sie haben einige große Strukturdefinitionen (die mehrere Cache-Zeilen umfassen), die Sie sicherstellen möchten, dass sie sowohl am Anfang als auch in der Mitte, in der sie die Grenze überschreiten, an der Cache-Zeilengrenze ausgerichtet sind.
Im Code können Sie sie mit GCC-Erweiterungen wie folgt deklarieren:
Sie möchten jedoch weiterhin sicherstellen, dass dies zur Laufzeit erzwungen wird.
Dies würde für eine einzelne Struktur funktionieren, aber es wäre schwierig, viele Strukturen abzudecken, von denen jede einen anderen Mitgliedsnamen hat, der ausgerichtet werden muss. Sie würden höchstwahrscheinlich den folgenden Code erhalten, in dem Sie die Namen des ersten Mitglieds jeder Struktur finden müssen:
Anstatt diesen Weg zu gehen, können Sie ein Array mit der Länge Null in der Struktur deklarieren, das als benannte Bezeichnung mit einem konsistenten Namen fungiert, aber keinen Speicherplatz belegt.
Dann wäre der Laufzeit-Assertionscode viel einfacher zu pflegen:
quelle