Ich verwende sizeof, um die Größe einer Struktur in C zu erhalten, aber das Ergebnis, das ich erhalten habe, ist unerwartet.
struct sdshdr {
int len;
int free;
char buf[];
};
int main(){
printf("struct len:%d\n",(sizeof(struct sdshdr)));
return 0;
} //struct len:8, with or without buf
Meine Frage ist, warum tut buf nehmen keinen Platz ein und warum ist die Größe der int Typ noch 4 auf einer 64-Bit-CPU?
hier ist die ausgabe von gcc -v:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.4.0
Thread model: posix
Das int bleiben 4 Bytes, long wird zum 8 Byte großen Datentyp. Um es zu erzwingen, verwenden Sie int32_t und int64_timho ist es drin stdint.h.
– Koshinae
9. Juli 2015 um 13:28 Uhr
@ Koshinae long ist nicht unbedingt 8 Bytes. Es kann die gleiche Größe haben wie intund das ist es oft. long longandererseits ist garantiert mindestens 8 Byte groß.
– Filipe Gonçalves
9. Juli 2015 um 13:29 Uhr
OT: Zu printf() a size_t Verwenden Sie den Konvertierungsbezeichner zu nicht d.
– alk
9. Juli 2015 um 13:32 Uhr
Siehe auch: stackoverflow.com/q/20221012/694576
– alk
9. Juli 2015 um 13:37 Uhr
Es gibt keine Garantie int wird 32 Bit sein. Dies hängt von der ABI (Application Binary Interface) Ihrer Plattform ab. Auch für die anderen Typen. Eben char kann größer sein. Dies ist der Grund, warum zu verwenden stdint.h Typen, wenn Sie eine bestimmte Größe für ein Objekt benötigen.
– zu ehrlich für diese Seite
9. Juli 2015 um 13:54 Uhr
Lundin
Das [] ist ein flexibles Array-Mitglied. Sie zählen nicht zur Gesamtgröße des Structs, da der C-Standard dies explizit vorschreibt:
6.7.2.1/18
Als Sonderfall kann das letzte Element einer Struktur mit mehr als einem benannten Element einen unvollständigen Array-Typ haben; Dies wird als flexibles Array-Member bezeichnet. In den meisten Situationen wird das flexible Arraymitglied ignoriert. Insbesondere ist die Größe der Struktur so, als ob das flexible Anordnungselement weggelassen worden wäre, außer dass es mehr nachlaufende Polsterung aufweisen kann, als das Weglassen implizieren würde.
Dies ist beabsichtigt, da der Zweck eines flexiblen Array-Mitglieds darin besteht, Ihnen zu ermöglichen, nachfolgende Daten dynamisch nach der Struktur zuzuweisen. (Wenn die Struktur ein Dateiheader, Protokollheader usw. ist)
Beispiel mit Diskussion über nicht standardmäßige gcc-Erweiterungen und den alten “struct hack” vor C99.
Huh! Wenn Sie zwei dieser Elemente nacheinander erstellen, sind sie dann 8 oder 16 Byte voneinander entfernt? Wo wird der Zeiger gespeichert? Oder nicht buf ein Zeiger? Was wäre, wenn ich es versuchen würde malloc Platz dazu? …
– Flori
9. Juli 2015 um 13:59 Uhr
@Floris buf ist kein Zeiger; es ist einfach das Ende der Struktur. Standardmäßig, buf[0] ist außerhalb des Platzes der Struktur; es ist deine Aufgabe malloc Daten jenseits der Struktur, um alles zu speichern, was Sie einfügen möchten buf.
– Oberst Zweiunddreißig
9. Juli 2015 um 14:04 Uhr
@ColonelThirtyTwo – danke. Meinst du das &buf[0]==&(struct)+sizeof(struct)? Wenn ja, wie würden Sie malloc dieser Raum? Es bedeutet im Grunde, dass Sie nicht einfach eine Variable mit diesem Typ erstellen können struct – Sie müssen den Zeiger tatsächlich malloc und umwandeln, der auf den Typ der Struktur zurückgegeben wird, oder? Scheint chaotisch – aber das sehe ich im verlinkten Beispiel.
– Flori
9. Juli 2015 um 14:09 Uhr
@Floris malloc(sizeof(mystruct) + sizeof(mystruct.flexiblearray[0]) * numelements) Ja, müsstest du verwenden malloc oder alloca; Bei einer regulären, vom Stapel zugewiesenen Struktur wäre kein Platz für das Array reserviert.
– Oberst Zweiunddreißig
9. Juli 2015 um 14:09 Uhr
@Ruslan Nicht, wenn Sie die Struktur dynamisch zuweisen, wie in dem von mir verlinkten Beispiel gezeigt.
– Ludin
10. Juli 2015 um 6:08 Uhr
Arkku
Ab C99 kann die Größe eines Arrays am Ende einer Struktur weggelassen werden. Für Zwecke von sizeof(struct) Dieses Array scheint die Größe Null zu haben (obwohl sein Vorhandensein der Struktur etwas Auffüllung hinzufügen kann), aber die Absicht ist, dass seine Länge so ist flexibel, dh bei der Zuweisung von Speicherplatz für die Struktur muss am Ende die gewünschte Menge an zusätzlichem Speicherplatz für das Array zugewiesen werden. (Um zu vermeiden, dass die Grenzen überschritten werden, sollte die tatsächlich zugewiesene Länge des Arrays irgendwo gespeichert werden.)
Vor C99 war es ein ziemlich üblicher Hack, ein Array der Größe 1 (oder 0, wo vom Compiler erlaubt) am Ende einer Struktur zu haben und dann mehr Platz dafür zuzuweisen, also erlaubte C99 diese Praxis ausdrücklich, indem es das einführte flexibles Array-Mitglied ohne Größenangabe.
Raman
Als GNU c-Erweiterung haben Sie Arrays der Länge Null:
Als GNU-Erweiterung kann die Anzahl der Elemente so klein wie null sein. Arrays der Länge Null sind nützlich als letztes Element einer Struktur, die eigentlich ein Header für ein Objekt mit variabler Länge ist:
struct line
{
int length;
char contents[0];
};
{
struct line *this_line = (struct line *)
malloc (sizeof (struct line) + this_length);
this_line -> length = this_length;
}
In ISO C99 würden Sie ein flexibles Array-Member verwenden, das sich in Syntax und Semantik leicht unterscheidet:
Flexible Array-Mitglieder werden als Inhalt geschrieben[] ohne die 0.
Flexible Array-Mitglieder haben einen unvollständigen Typ, daher kann der sizeof-Operator nicht angewendet werden. Als Eigenart der ursprünglichen Implementierung von Arrays der Länge Null wird sizeof zu Null ausgewertet.
Flexible Array-Mitglieder dürfen nur als letztes Mitglied einer Struktur erscheinen, die ansonsten nicht leer ist.
Eine Struktur, die ein flexibles Array-Element enthält, oder eine Union, die eine solche Struktur enthält (möglicherweise rekursiv), darf kein Element einer Struktur oder eines Elements eines Arrays sein. (Diese Verwendungen sind jedoch von GCC als Erweiterungen erlaubt.)
Das ist falsch. Dies ist kein Array der Größe Null, sondern ein flexibles Array-Mitglied. Arrays der Größe Null sind nicht in C erlaubt (es gibt eine gcc-Nicht-Standard-Erweiterung, die sie erlaubt, aber das ist ein Überbleibsel aus der Zeit vor C99).
– Ludin
9. Juli 2015 um 13:27 Uhr
@ARBY: Sie verwirren gcc Arrays der Größe Null und flexibles Array-Mitglied. Wie Lundin schrieb, sind dies zwei ähnliche Lösungen für dasselbe Problem (sie unterscheiden sich tatsächlich in der gemeldeten Größe). Lesen Sie die gcc Handbuch. Ein Array der Größe Null sollte als Legacy angesehen werden. Verwenden Sie sie nicht mehr für neuen Code.
– zu ehrlich für diese Seite
9. Juli 2015 um 13:37 Uhr
Sei es wie es mag, buf[0] ist für die Frage nicht relevant, da das OP dies getan hat buf[]…
– Arkku
9. Juli 2015 um 13:42 Uhr
Ich habe die Ablehnung bereits entfernt. Ich denke jedoch, dass es eine schlechte Idee ist, diese gcc-Erweiterung ins Spiel zu bringen, da OP explizit a verwendet flexibles Array-Mitglied das ist schon der Weg des Standards und die Erweiterung bringt keinen Mehrwert.
– zu ehrlich für diese Seite
9. Juli 2015 um 14:34 Uhr
@ARBY Nein, das tut es nicht, das OP hat es sizeof(struct) Das ist nicht null, sondern die Größe der Struktur ohne das flexible Element. Dies ist im C99-Standard festgelegt und hat nichts mit der GNU-Erweiterung zu tun. In der Zwischenzeit sagt der hervorgehobene Teil in Ihrer Antwort das aus sizeof(struct.buf) wird aufgrund der Legacy-Implementierung mit Arrays der Länge Null zu Null ausgewertet, aber (wie es direkt davor in dem von Ihnen zitierten Text heißt) sizeof(struct.buf) ist tatsächlich ungültig, weil das flexible Mitglied einen unvollständigen Typ hat (und zB clang einen Kompilierungsfehler ausgibt, wenn Sie es versuchen).
– Arkku
9. Juli 2015 um 15:58 Uhr
buf hier ist ein flexible array member
Flexible Array-Mitglieder haben einen unvollständigen Typ, sodass der sizeof-Operator möglicherweise nicht angewendet wird, während die ursprüngliche Implementierung von zero-length arrays, sizeof evaluates to zero.
14019800cookie-checkInterner Mechanismus von sizeof in C?yes
Das
int
bleiben 4 Bytes,long
wird zum 8 Byte großen Datentyp. Um es zu erzwingen, verwenden Sieint32_t
undint64_t
imho ist es drinstdint.h
.– Koshinae
9. Juli 2015 um 13:28 Uhr
@ Koshinae
long
ist nicht unbedingt 8 Bytes. Es kann die gleiche Größe haben wieint
und das ist es oft.long long
andererseits ist garantiert mindestens 8 Byte groß.– Filipe Gonçalves
9. Juli 2015 um 13:29 Uhr
OT: Zu
printf()
asize_t
Verwenden Sie den Konvertierungsbezeichnerzu
nichtd
.– alk
9. Juli 2015 um 13:32 Uhr
Siehe auch: stackoverflow.com/q/20221012/694576
– alk
9. Juli 2015 um 13:37 Uhr
Es gibt keine Garantie
int
wird 32 Bit sein. Dies hängt von der ABI (Application Binary Interface) Ihrer Plattform ab. Auch für die anderen Typen. Ebenchar
kann größer sein. Dies ist der Grund, warum zu verwendenstdint.h
Typen, wenn Sie eine bestimmte Größe für ein Objekt benötigen.– zu ehrlich für diese Seite
9. Juli 2015 um 13:54 Uhr