Wie kann diese Struktur eine Größe von == 0 haben?

80

Es gibt einen alten Beitrag, der nach einem Konstrukt fragt, für das er zurückkehren sizeofwürde 0. Es gibt einige Highscore-Antworten von Benutzern mit hohem Ansehen, die besagen, dass standardmäßig kein Typ oder keine Variable eine Größe von 0 haben kann. Und ich stimme dem zu 100% zu.

Es gibt jedoch diese neue Antwort, die diese Lösung darstellt:

struct ZeroMemory {
    int *a[0];
};

Ich wollte gerade abstimmen und es kommentieren, aber die Zeit, die ich hier verbracht habe, hat mich gelehrt, selbst die Dinge zu überprüfen, bei denen ich mir zu 100% sicher bin. Also ... zu meiner Überraschung beide gccund clangzeigen die gleichen Ergebnisse : sizeof(ZeroMemory) == 0. Darüber hinaus ist die Größe einer Variablen 0:

ZeroMemory z{};
static_assert(sizeof(z) == 0); // Awkward...

Was ...?

Godbolt Link

Wie ist das möglich?

Bolov
quelle
37
Das Array mit der Größe Null ist kein Standard-C ++. aber Erweiterung.
Jarod42
@ Jarod42 jetzt ist es offensichtlich.
Bolov
1
Es wird auch nicht unter MSVC kompiliert
UnholySheep
7
Fügen Sie sie nun in ein Array ein und führen Sie eine Zeigerarithmetik durch.
CodesInChaos
Hmmm ... Wie viele Bytes sollte nichts nehmen, wenn nicht 0?
Gerhardh

Antworten:

52

Bevor C standardisiert wurde, hätten viele Compiler keine Schwierigkeiten gehabt, mit Typen mit der Größe Null umzugehen, solange der Code nie versucht hätte, einen Zeiger auf einen Typ mit der Größe Null von einem anderen zu subtrahieren. Solche Typen waren nützlich, und ihre Unterstützung war einfacher und billiger als das Verbot. Andere Compiler haben sich jedoch entschlossen, solche Typen zu verbieten, und einige statische Assertionscodes haben sich möglicherweise auf die Tatsache gestützt, dass sie kreischen würden, wenn Code versuchen würde, ein Array mit der Größe Null zu erstellen. Die Autoren des Standards standen vor der Wahl:

  1. Ermöglichen Sie Compilern, stillschweigend Array-Deklarationen mit der Größe Null zu akzeptieren, selbst wenn der Zweck solcher Deklarationen darin besteht, eine Diagnose auszulösen und die Kompilierung abzubrechen, und verlangen Sie, dass alle Compiler solche Deklarationen akzeptieren (wenn auch nicht unbedingt stillschweigend), um Objekte mit der Größe Null zu erzeugen .

  2. Ermöglichen Sie Compilern, Array-Deklarationen mit der Größe Null stillschweigend zu akzeptieren, selbst wenn der Zweck solcher Deklarationen darin besteht, eine Diagnose- und Abbruchkompilierung auszulösen, und Compilern, die auf solche Deklarationen stoßen, die Kompilierung entweder abzubrechen oder nach Belieben fortzusetzen.

  3. Erfordern, dass Implementierungen eine Diagnose ausgeben, wenn Code ein Array mit der Größe Null deklariert, aber dann Implementierungen erlauben, die Kompilierung entweder abzubrechen oder nach Belieben fortzusetzen (mit welcher Semantik sie dies für richtig halten).

Die Autoren des Standards entschieden sich für # 3. Folglich werden Array-Deklarationen mit der Größe Null von der Standard- "Erweiterung" berücksichtigt, obwohl solche Konstrukte weitgehend unterstützt wurden, bevor der Standard sie verbot.

Der C ++ - Standard erlaubt das Vorhandensein leerer Objekte. Um jedoch zu ermöglichen, dass die Adressen leerer Objekte als Token verwendet werden können, muss sie eine Mindestgröße von 1 haben. Für ein Objekt ohne Mitglieder muss eine Größe von 1 vorhanden sein 0 würde somit den Standard verletzen. Wenn ein Objekt jedoch Elemente mit der Größe Null enthält, stellt der C ++ - Standard keine Anforderungen an die Verarbeitung, außer dass ein Programm, das eine solche Deklaration enthält, eine Diagnose auslösen muss. Da der meiste Code, der solche Deklarationen verwendet, erwartet, dass die resultierenden Objekte eine Größe von Null haben, besteht das nützlichste Verhalten für Compiler, die solchen Code erhalten, darin, sie so zu behandeln.

Superkatze
quelle
"Bevor C standardisiert wurde", meinen Sie, bevor C99 existiert?
Stargateur
1
@ Stargateur: Ich meine die ungefähr 15 Jahre zwischen der Erfindung von C und der Erstellung des C89-Standards. C99 fügte eine begrenzte Form von Objekten mit der Größe Null zurück, um einige der Kluds zu vermeiden, die erforderlich sind, um das Verbot von Arrays mit der Größe Null zu umgehen. Solche Kludges wären jedoch nicht erforderlich gewesen, wenn C89 Arrays mit der Größe Null nicht störend verboten hätte den ersten Platz.
Supercat
"Solche Typen waren nützlich" Wie waren sie nützlich?
user109923
1
@ user109923: Als Beispiel : struct polygon { int count; POINT sides[0];}; struct { struct polygon poly; POINT pts[3]; } myTriangle = { {3}, {{1,1},{2,2},{2,1} };. Man müsste vorsichtig sein, um sicherzustellen, dass die Ausrichtung keine Probleme verursacht, aber man könnte Objekte mit statischer Dauer unterschiedlicher Größe haben, die austauschbar verarbeitet werden könnten.
Supercat
42

Wie von Jarod42 hervorgehoben, handelt es sich bei Arrays mit der Größe Null nicht um Standard-C ++, sondern um GCC- und Clang-Erweiterungen.

Das Hinzufügen -pedanticerzeugt diese Warnung:

5 : <source>:5:12: warning: zero size arrays are an extension [-Wzero-length-array]
    int *a[0];
           ^

Ich vergesse immer, dass std=c++XX(statt std=gnu++XX) nicht alle Erweiterungen deaktiviert werden.

Dies erklärt das sizeofVerhalten immer noch nicht . Aber zumindest wissen wir, dass es nicht Standard ist ...

Bolov
quelle
1
In beiden Fällen ist es überraschend, dass selbst eine leere Struktur eine Wertgröße ungleich Null erzeugen sollte ...
WF
2
Interessanterweise werden zwei automatische Instanzen getrennt gespeichert sizeof(int*)(ich nehme an): coliru.stacked-crooked.com/a/e9a3038bf587b65c
eerorika
9
Dies beantwortet nur, dass ein Array mit der Größe Null nicht Standard ist, erklärt jedoch nicht die von Ihnen gestellte Frage.
Haccks
1
Das Verhalten muss in keiner Weise gemäß dem Standard sinnvoll sein - es muss nur für die Compiler-Implementierer sinnvoll sein. Der Compiler erlaubt bereits, einen Typ auf eine Weise zu definieren, die vom Standard verboten ist. Daher gelten auch die Anforderungen der Norm, was das Ergebnis von sizeoffür diesen Typ ergibt, nicht. Das Verhalten ist daher eine Entscheidung der Compiler-Entwickler.
Peter
1
Die Idee ist ZeroMemory* z = (ZeroMemory*)malloc(sizeof(ZeroMemory) + 2 * sizeof(int*)); z.a[1] = new int{1}; /* or whatever, just use indices into a to access dynamic memory after the previous members (in this case: none) */natürlich, dass dies nicht standardkompatibel ist! (Dies kann zum einfachen Parsen von Netzwerknachrichten mit dynamischer Länge verwendet werden, indem beispielsweise in eine Struktur umgewandelt wird.)
hoffmale
19

In C ++ ist ein Array mit der Größe Null unzulässig.

ISO / IEC 14882: 2003 8.3.4 / 1:

[..] Wenn der konstante Ausdruck (5.19) vorhanden ist, muss er ein integraler konstanter Ausdruck sein und sein Wert muss größer als Null sein . Der konstante Ausdruck gibt die Grenze (Anzahl der Elemente im Array) an. Wenn der Wert des konstanten Ausdrucks ist N, hat das Array NElemente, die mit nummeriert 0sind N-1, und der Typ des Bezeichners von Dist " abgeleitetes Deklarator-Typ-Listen- Array von NT". [..]

Für g ++ muss das -pedanticFlag eine Warnung für ein Array mit der Größe Null ausgeben.

msc
quelle
5

Arrays mit der Länge Null sind eine Erweiterung von GCC und Clang. Das Anwenden sizeofauf Arrays mit der Länge Null wird mit Null bewertet .

Eine C ++ - Klasse (leer) kann keine Größe 0haben. Beachten Sie jedoch, dass die Klasse ZeroMemorynicht leer ist. Es hat ein benanntes Mitglied mit Größe 0und die Anwendung sizeofgibt Null zurück.

Haccks
quelle
5
Also ... eine leere Klasse kann keine Größe haben 0, aber eine nicht leere Klasse kann Größe haben 0... das macht nicht viel Sinn. Aber ich denke, dies ist die Art von widersprüchlichem Randfall, den Sie bei nicht standardmäßigen Erweiterungen erhalten.
Bolov
@bolov; Eine c ++ - Klasse (leer) kann nicht die Größe 0 haben. Dies entspricht dem Standard für leere Klassen. Der C ++ - Standard besagt dies, weil es keine andere Möglichkeit gibt, eine Struktur mit Größe zu erstellen 0. In diesem speziellen Fall hat das Mitglied von struct it self die Größe 0 und dies macht die Größe von struct 0. Ich weiß, dass es verwirrend ist, aber ich habe mein Bestes versucht, es zu erklären.
Haccks
3
@bodov: Aber dies ist keine C ++ - Klasse, sondern eine G ++ - Klasse, daher müssen die C ++ - Regeln nicht darauf angewendet werden. Beachten Sie insbesondere, dass Sie kein Array dieses Typs haben oder mit seinen Zeigern rechnen können, sodass die übliche Argumentation, dass die Größe nicht Null sein kann, auch nicht gilt.
Ben Voigt