Warum macht das:
#include <stdio.h>
#include <limits.h>
#include <inttypes.h>
int main() {
enum en_e {
en_e_foo,
en_e_bar = UINT64_MAX,
};
enum en_e e = en_e_foo;
printf("%zu\n", sizeof en_e_foo);
printf("%zu\n", sizeof en_e_bar);
printf("%zu\n", sizeof e);
}
Drucken 4 8 8
in C und 8 8 8
in C ++ (auf einer Plattform mit 4 Byte Ints)?
Ich hatte den Eindruck, dass die UINT64_MAX
Zuweisung alle Aufzählungskonstanten auf mindestens 64 Bit zwingen würde, aber en_e_foo
in Ebene C bei 32 bleibt.
Was ist der Grund für die Diskrepanz?
Antworten:
In C ist eine
enum
Konstante vom Typint
. In C ++ ist es vom Aufzählungstyp.In C handelt es sich um eine Einschränkungsverletzung , für die eine Diagnose erforderlich ist ( wenn diese
UINT64_MAX
überschritten wirdINT_MAX
, was höchstwahrscheinlich der Fall ist). Der AC-Compiler kann das Programm insgesamt ablehnen oder eine Warnung ausgeben und dann eine ausführbare Datei generieren, deren Verhalten undefiniert ist. (Es ist nicht 100% klar, dass ein Programm, das gegen eine Einschränkung verstößt, notwendigerweise ein undefiniertes Verhalten aufweist. In diesem Fall gibt der Standard jedoch nicht an, wie das Verhalten ist. Das ist also immer noch undefiniertes Verhalten.)gcc 6.2 warnt nicht davor. Klirren tut. Dies ist ein Fehler in gcc; Einige Diagnosemeldungen werden fälschlicherweise gesperrt, wenn Makros aus Standardheadern verwendet werden. Vielen Dank an Grzegorz Szpetkowski für das Auffinden des Fehlerberichts: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613
In C ++ hat jeder Aufzählungstyp einen zugrunde liegenden Typ , der ein ganzzahliger Typ ist (nicht unbedingt
int
). Dieser zugrunde liegende Typ muss in der Lage sein, alle konstanten Werte darzustellen. In diesem Fall sind also beideen_e_foo
unden_e_bar
vom Typen_e
, der mindestens 64 Bit breit sein muss, auch wenn erint
schmaler ist.quelle
UINT64_MAX
nicht zu überschreitenINT_MAX
,int
sind mindestens 65 Bit erforderlich .-Wpedantic
und18446744073709551615ULL
aber nicht mit ausgibtUINT64_MAX
.int
muss ein vorzeichenbehafteter Typ sein, daher müssen mindestens 65 Bit vorhanden sein, um dargestellt werden zu könnenUINT64_MAX
(2 ** 64-1).en_e_bar
ist nicht größer als die Aufzählung,en_e_foo
ist kleiner. Die Enum-Variable war so groß wie die größte Konstante.Dieser Code ist in erster Linie kein gültiges C.
In Abschnitt 6.7.2.2 in C99 und C11 heißt es:
Eine Compilerdiagnose ist obligatorisch, da es sich um eine Einschränkungsverletzung handelt, siehe 5.1.1.3:
quelle
Während in C a
enum
als separater Typ betrachtet wird, haben Enumeratoren selbst immer einen Typint
.Das angezeigte Verhalten ist also eine Compiler-Erweiterung.
Ich würde sagen, es ist sinnvoll, die Größe eines der Enumeratoren nur zu erweitern, wenn sein Wert zu groß ist.
Andererseits haben in C ++ alle Enumeratoren den Typ, in dem
enum
sie deklariert sind.Aus diesem Grund muss die Größe jedes Enumerators gleich sein. Daher wird die Gesamtgröße
enum
erweitert, um den größten Enumerator zu speichern.quelle
Wie andere betonten, ist der Code (in C) aufgrund einer Einschränkungsverletzung schlecht geformt.
Es gibt den GCC-Fehler Nr. 71613 (gemeldet im Juni 2016), der besagt, dass einige nützliche Warnungen mit Makros zum Schweigen gebracht werden.
Die aktuelle Problemumgehung besteht möglicherweise darin, dem Makro einen unären
+
Operator voranzustellen :Dies führt zu einem Kompilierungsfehler auf meinem Computer mit GCC 4.9.2:
quelle
C11 - 6.7.2.2/2
en_e_bar=UINT64_MAX
ist eine Einschränkungsverletzung und dies macht den obigen Code ungültig. Eine Diagnosemeldung sollte erstellt werden, indem die Implementierung gemäß dem C11-Entwurf bestätigt wird:Es scheint, dass GCC einen Fehler hat und die Diagnosemeldung nicht erstellt werden konnte. ( Fehler wird in der Antwort von Grzegorz Szpetkowski gezeigt
quelle
sizeof
ist ein Operator zur Kompilierungszeit. Hier gibt es keine UB, und selbst wenn es eine gäbe, könnte dies keinen Einfluss habensizeof
.short s = 0xdeadbeef
), und das Verhalten durch die Implementierung definiert ist.Ich habe mir die Standards angesehen und mein Programm scheint aufgrund von 6.7.2.2p2 eine Einschränkungsverletzung in C zu sein :
und in C ++ aufgrund von 7.2.5 definiert:
quelle