Wie groß ist die Leere?

77

Was würde diese Aussage bringen?

void *p = malloc(sizeof(void));

Bearbeiten: Eine Erweiterung der Frage.

Wenn sizeof (void) im GCC-Compiler 1 ergibt, wird 1 Byte Speicher zugewiesen und der Zeiger p zeigt auf dieses Byte und würde p ++ auf 0x2346 erhöht werden? Angenommen, p war 0x2345. Ich spreche von p und nicht von * p.

Lakshmi
quelle
4
Zu Ihrer Information. Dies ist kein Standard C. In vielen Compilern wäre dies ein Fehler zur Kompilierungszeit.
Johan Kotlinski
Warum willst du das wissen. Das Verständnis kann uns helfen, die Frage zu beantworten.
Martin York
4
Kommentar zu Ihrer Bearbeitung: Ja, in GCC (nur) erhöht das Inkrementieren eines ungültigen Zeigers den Wert um eins. Wenn Sie die Portabilität Ihres Codes schätzen, missbrauchen Sie nicht die Freiheit, die GCC Ihnen gewährt. Es ist völlig unüblich. Und GCC gibt dies auch mit '-std = c99 -pedantic' zu.
Jonathan Leffler

Antworten:

56

Der Typ voidhat keine Größe; das wäre ein Kompilierungsfehler. Aus dem gleichen Grund können Sie so etwas nicht tun:

void n;

BEARBEITEN. Zu meiner Überraschung, tut sizeof(void)eigentlich nicht in GNU C kompilieren:

$ echo 'int main() { printf("%d", sizeof(void)); }' | gcc -xc -w - && ./a.out 
1

In C ++ ist dies jedoch nicht der Fall:

$ echo 'int main() { printf("%d", sizeof(void)); }' | gcc -xc++ -w - && ./a.out 
<stdin>: In function 'int main()':
<stdin>:1: error: invalid application of 'sizeof' to a void type
<stdin>:1: error: 'printf' was not declared in this scope
reko_t
quelle
31
Aber GCC hat die Option, 'sizeof (void) == sizeof (char)' zu behandeln ... Siehe -Wpointer-arith
Jonathan Leffler
5
@ Jon Dies ist für Narren aus den frühen 80ern, die gerade erst anfingen zu lernen, wie man programmiert. Standards wurden nicht allgemein festgelegt.
Unixman83
5
sizeof (void)ist eine Einschränkungsverletzung. Ein konformer C-Compiler (der gcc ist nicht standardmäßig) muss zumindest davor warnen und kann (und sollte IMHO sollte) dies ablehnen. Referenz: N1570 6.5.3.4p1 ( voidist ein unvollständiger Typ). Dies ist seit dem 1989 eingeführten Standard der Fall void.
Keith Thompson
3
@ unixman83: Das macht keinen Sinn. voidexistierte nicht, bevor es durch den ANSI C-Standard von 1989 eingeführt wurde, den gleichen Standard, der sizeof (void)eine Einschränkungsverletzung verursachte.
Keith Thompson
1
Sizeof void wird kompiliert, da Sie es benötigen, um mit void * -Zeigern zu arbeiten.
Kelin
41

Wenn Sie GCC verwenden und keine Kompilierungsflags verwenden, mit denen compilerspezifische Erweiterungen entfernt werden, sizeof(void)ist dies 1. GCC verfügt über eine nicht standardmäßige Erweiterung , die dies tut.

Im Allgemeinen voidhandelt es sich um einen unvollständigen Typ, und Sie können sizeof nicht für unvollständige Typen verwenden.

hrnt
quelle
2
Die Option -Wpointer-arithist impliziert durch -pedantic. Ich habe es immer benutzt -pedantic. :)
Josh Lee
1
@jleedev In der Tat benutze ich -pedantic so oft ich kann. Leider machen einige Bibliotheks-Header von Drittanbietern gcc -pedantic sehr traurig :(
hrnt
1
+1 Warum hat GCC wirklich Tausende nutzloser Erweiterungen?
@ user142019: weil sie nützlich sind? Und hilft dabei, ein konsistentes Backend-Modell durch verschiedene Programmiersprachen und Hardwarearchitekturen zu erhalten.
kriss
16

Obwohl voides für einen Typ an Ort und Stelle stehen kann, kann es tatsächlich keinen Wert enthalten. Daher hat es keine Größe im Speicher. Das Abrufen der Größe von a voidist nicht definiert.

Ein void Zeiger ist einfach ein Sprachkonstrukt, das einen Zeiger auf untypisierten Speicher bedeutet.

Konrad Rudolph
quelle
1
Na und ? Sollte keine leere Struktur auch keine Größe im Speicher verbrauchen und das sollte ein Problem sein. Nun, eigentlich frage ich mich, ob leere Strukturen in C ++ wirklich 0 Bytes verwenden (wie in C), wie sizeof 1 für sie zurückgibt.
kriss
1
@kriss Ich bin mir nicht sicher, was Sie sagen, aber wie Sie selbst bemerkt haben, belegen leere Strukturen aus Gründen der Objektidentität den Speicher.
Konrad Rudolph
1
in der Tat, aber es war nicht wahr in C und die Verwendung von Speicher für leere Objekte fühlt sich böse an.
kriss
13

voidhat keine Größe. Sowohl in C als auch in C ++ ist der Ausdruck sizeof (void)ungültig.

In C unter Angabe von N1570 6.5.3.4 Absatz 1:

Der sizeofOperator darf nicht auf einen Ausdruck angewendet werden, der einen Funktionstyp oder einen unvollständigen Typ hat , auf den Namen eines solchen Typs in Klammern oder auf einen Ausdruck, der ein Bitfeldelement bezeichnet.

(N1570 ist ein Entwurf der ISO C-Norm von 2011.)

voidist ein unvollständiger Typ. Dieser Absatz ist eine Einschränkung , was bedeutet, dass jeder konforme C-Compiler eine Verletzung diagnostizieren muss. (Die Diagnosemeldung kann eine nicht tödliche Warnung sein.)

Der C ++ 11-Standard hat einen sehr ähnlichen Wortlaut. Beide Ausgaben wurden veröffentlicht, nachdem diese Frage gestellt wurde, aber die Regeln gehen auf den ANSI C-Standard von 1989 und die frühesten C ++ - Standards zurück. Tatsächlich reicht die Regel, voiddie ein unvollständiger Typ ist, auf den sizeofmöglicherweise nicht angewendet wird, genau bis zur Einführung voidin die Sprache zurück.

gcc hat eine Erweiterung , die sizeof (void)als 1 behandelt wird . gcc ist standardmäßig kein konformer C-Compiler, daher wird im Standardmodus nicht gewarnt sizeof (void). Erweiterungen wie diese sind auch für vollständig konforme C-Compiler zulässig, die Diagnose ist jedoch weiterhin erforderlich.

Keith Thompson
quelle
Ich denke, dass sie sizeof(void) == 1Konsistenz mit Zeigerarithmetik auf voidZeiger zugelassen haben, das ist auch eine gcc-Erweiterung . Unter Berufung auf ihre Dokumentation : A consequence of this is that sizeof is also allowed on void and on function types, and returns 1.. Trotzdem sollte für C standardmäßig eine Diagnosemeldung vorhanden sein, wie es der Standard erfordert.
Grzegorz Szpetkowski
2
@GrzegorzSzpetkowski: (Antwort einige Jahre später.) Ja, der Standard erfordert eine Diagnose, aber gcc ist standardmäßig kein konformer C-Compiler. Der C-Standard stellt logischerweise keine Anforderungen an Implementierungen, die nicht behaupten, ihm zu entsprechen. gcc kann so eingestellt werden, dass es (fast) den richtigen Befehlszeilenoptionen entspricht, wie z -std=c11 -pedantic. Mit solchen Optionen wird die erforderliche Diagnose ausgegeben.
Keith Thompson
6

sizeof()kann nicht auf unvollständige Typen angewendet werden. Und voidist ein unvollständiger Typ, der nicht abgeschlossen werden kann.

Aleksei Potov
quelle
3

In C, sizeof(void) == 1in GCC, aber dies scheint von Ihrem Compiler abzuhängen.

In C ++ bekomme ich:

In der Funktion 'int main ()':
Zeile 2: Fehler: Ungültige Anwendung von 'sizeof' auf einen ungültigen Typ
Kompilierung wegen schwerwiegender Fehler abgebrochen.
Greg Hewgill
quelle
3
Nur in GCC ist sizeof (void) == 1; Im Standard ist es undefiniertes Verhalten.
Jonathan Leffler
Aktualisiert, um eine GCC-spezifische Notiz hinzuzufügen.
Greg Hewgill
2
@ JonathanLeffler: Es ist nicht nur undefiniertes Verhalten. Es ist eine Einschränkungsverletzung. N1570 6.5.3.4p1.
Keith Thompson
Es ist nicht nur GCC, sondern auch GCC und alle anderen Compiler, die so konzipiert sind, dass sie damit kompatibel sind (einschließlich clang, icc, tcc und wahrscheinlich anderer).
Keith Thompson
0

Zum 2. Teil der Frage: Beachten Sie, dass sizeof (void *)! = Sizeof (void). In einem 32-Bit-Bogen beträgt sizeof (void *) 4 Byte, daher wird p ++ entsprechend festgelegt. Der Betrag, um den ein Zeiger inkrementiert wird, hängt von den Daten ab, auf die er zeigt. Es wird also um 1 Byte erhöht.


quelle
1
p zeigte also auf 1-Byte-Daten, da sizeof (void) 1 ergibt, also bedeutet p ++, dass p um 1 und nicht um 4 ??
Lakshmi
1
Der Betrag, um den ein Zeiger erhöht wird, hängt von den Daten ab, auf die er zeigt. Also ja.
p ist vom Typ void * (void pointer), nicht void. Es würde also um die Größe des Typs void * erhöht (normalerweise 4 in 32-Bit-Systemen).
Technowise
4
@Technowise: nein. Jeder Zeiger vom Typ T*wird um inkrementiert sizeof(T)(und nicht sizeof(T*) , was fast immer gleich wäre, außer vielleicht für Funktionszeiger), wenn er inkrementiert wird. Die Ausnahme ist natürlich void(und wieder ist die Ausnahme, wenn die GCC-Erweiterungen aktiviert sind).
Konrad Rudolph
1
@Amit: Wie sind Sie zu dem Schluss gekommen, dass die Größe der Daten, auf die ein void*Zeiger zeigt, 1 Byte beträgt? sizeof (void)ist eine Einschränkungsverletzung. (Eine gcc-Erweiterung behandelt es als 1.)
Keith Thompson
-1

Während sizeof (void) an sich vielleicht keinen Sinn ergibt, ist es wichtig, wenn Sie Zeiger rechnen.

z.B.

 void *p;
 while(...)
      p++;

Wenn sizeof (void) als 1 betrachtet wird, funktioniert dies. Wenn sizeof (void) als 0 betrachtet wird, treffen Sie eine Endlosschleife.

user906752
quelle
4
Das Beispielcode-Snippet ist in C und C ++ unzulässig. Einige Compiler erlauben es trotzdem.
James Roth
1
Nein, es ist überhaupt nicht wichtig (obwohl die Autoren von gcc anscheinend dachten, dass es so ist). Wenn Sie Zeigerarithmetik für Bytezeiger ausführen möchten, verwenden Sie unsigned char*.
Keith Thompson
@KeithThompson - Es charwird nicht garantiert, dass 1 Byte vorhanden ist. Ich habe an einem System gearbeitet, das es auf 32 Bit eingestellt hat.
Asche
@ash: Ja, chargarantiert 1 Byte. Wenn Sie auf einem 32-Bit- charSystem gearbeitet haben, beträgt ein Byte auf diesem System 32 Bit ( CHAR_BIT == 32). So definiert der C-Standard "Byte". C - Standard, 6.5.3.4 Absätze 2 und 4: „Der sizeofOperator liefert die Größe (in Bytes) des Operanden [...] , wenn. sizeofWird auf einen Operanden angewandt , die Aktivität hat char, unsigned charoder signed char, (oder eine qualifizierte Version davon) das Ergebnis ist 1. "
Keith Thompson
Wann kam der C-Standard 6.5.3.4 heraus? Ich habe Mitte der 90er Jahre an diesem System gearbeitet.
Asche
-1

Die meisten C ++ - Compiler haben sich entschieden, beim Abrufen einen Kompilierungsfehler auszulösen sizeof(void).

Beim Kompilieren von C ist gcc nicht konform und wird sizeof(void)als 1 definiert . Es mag seltsam aussehen, hat aber eine Begründung. Wenn Sie eine Zeigerarithmetik ausführen, bedeutet das Hinzufügen oder Entfernen einer Einheit das Hinzufügen oder Entfernen des Objekts, auf das die Größe zeigt. Das Definieren sizeof(void)als 1 hilft also beim Definieren void*als Zeiger auf das Byte (untypisierte Speicheradresse). Andernfalls würden Sie überraschende Verhaltensweisen haben, wenn Sie Zeigerarithmetik wie p+1 == p whenp verwenden void*. Eine solche Zeigerarithmetik für leere Zeiger ist in c ++ nicht zulässig, funktioniert jedoch gut, wenn C mit gcc kompiliert wird.

Die empfohlene Standardmethode wäre die Verwendung char*für diesen Zweck (Zeiger auf Byte).

Ein weiterer ähnlicher Unterschied zwischen C und C ++ bei Verwendung von sizeof tritt auf, wenn Sie eine leere Struktur wie folgt definiert haben:

struct Empty {
} empty;

Wenn Sie gcc als meinen C-Compiler verwenden, wird sizeof(empty)0 zurückgegeben. Wenn Sie g ++ verwenden, gibt derselbe Code 1 zurück.

Ich bin nicht sicher, was in diesem Punkt sowohl C- als auch C ++ - Standards angibt, aber ich glaube, dass das Definieren der Größe einiger leerer Strukturen / Objekte bei der Referenzverwaltung hilft, um zu vermeiden, dass zwei Verweise auf unterschiedliche aufeinanderfolgende Objekte, von denen das erste leer ist, die erhalten die gleiche Anschrift. Wenn Referenzen mithilfe versteckter Zeiger implementiert werden, wie dies häufig der Fall ist, hilft das Sicherstellen einer anderen Adresse beim Vergleichen.

Dies vermeidet jedoch lediglich ein überraschendes Verhalten (Vergleich von Referenzen in Eckfällen), indem ein anderes eingeführt wird (leere Objekte, selbst PODs verbrauchen mindestens 1 Byte Speicher).

kriss
quelle
6
"C hat stattdessen sizeof (void) als 1 definiert." - Nein das ist falsch. sizeof(void)ist in C nicht wie in C ++ definiert. GCC stimmt in diesem Punkt einfach nicht überein, und die Zeigerarithmetik für voidZeiger ist ebenfalls einfach nicht definiert - void* p; p++;ist ungültig. Sogar GCC gibt Ihnen beim Kompilieren mit dem -pedanticFlag eine Warnung . Und Sie irren sich auch über das Standardverhalten von GCC für C ++: Standardmäßig wird dies als Fehler behandelt .
Konrad Rudolph
@Konrad: Ja, ich bin mir ziemlich sicher, dass dies bei älteren Versionen von gcc nicht der Fall war, aber aktuelle tun, was Sie sagen.
Kriss
Zumindest in C ist eine leere structDefinition ein Syntaxfehler. Die Unterstützung von gcc ist eine Erweiterung. Sie können in C ++ gültig sein; Ich bin mir nicht sicher.
Keith Thompson