Angenommen, ich habe (auf einer 32-Bit-Maschine)
enum foo {
val1 = 0x7FFFFFFF, // originally '2^31 - 1'
val2,
val3 = 0xFFFFFFFF, // originally '2^32 - 1'
val4,
val5
};
Was ist der Wert von val2, val4 und val5? Ich weiß, ich könnte es testen, aber das Ergebnis ist standardisiert?
Im C-Standard:
C11 (n1570), § 6.7.2.2 Aufzählungsbezeichner
Jeder aufgezählte Typ muss mit kompatibel sein char
, ein ganzzahliger Typ mit Vorzeichen oder ein ganzzahliger Typ ohne Vorzeichen. Die Wahl des Typs ist implementierungsdefiniert, aber muss in der Lage sein, die Werte aller Mitglieder der Aufzählung darzustellen.
Wenn der vom Compiler verwendete zugrunde liegende Typ diese Werte nicht darstellen kann, ist das Verhalten nicht definiert.
C11 (n1570), § 4. Konformität
Wenn eine „soll“- oder „soll nicht“-Anforderung, die außerhalb einer Beschränkung oder einer Laufzeitbeschränkung erscheint, verletzt wird, ist das Verhalten undefiniert.
Aus dem C++11-Standard (§7.2,6Hervorhebung von mir):
Für eine Aufzählung, deren zugrunde liegender Typ nicht festgelegt ist, Der zugrunde liegende Typ ist ein ganzzahliger Typ, der alle Enumeratorwerte darstellen kann in der Aufzählung definiert. Wenn kein ganzzahliger Typ alle Enumeratorwerte darstellen kann, ist die Enumeration falsch formatiert. Es ist implementierungsdefiniert, welcher ganzzahlige Typ als zugrunde liegender Typ verwendet wird, außer dass der zugrunde liegende Typ nicht größer als int sein darf, es sei denn, der Wert eines Enumerators passt nicht in ein int oder unsigned int.
Der Compiler wird also gerne das Richtige tun, wenn es einen ganzzahligen Typ gibt, der größer als 32 Bit ist. Wenn nicht, ist die Aufzählung falsch formatiert. Es wird kein Umwickeln geben.
Die Werte werden sein:
enum foo {
val1 = 0x7FFFFFFF,
val2, // 0x80000000 = 2^31
val3 = 0xFFFFFFFF,
val4, //0x0000000100000000 = 2^32
val5 //0x0000000100000001 = 2^32+1
};
Die ansteigenden Zahlen sind ebenfalls gut definiert (§7.2,2):
[…] Eine Enumerator-Definition ohne Initialisierer gibt dem Enumerator den Wert, den man erhält, indem man den Wert des vorherigen Enumerators um eins erhöht.
C99 / C11
Auftakt:
5.2.4.2.1 erfordert int
sein wenigstens 16 Bit breit; AFAIK, es gibt keine Obergrenze (long
muss aber länger oder gleich sein, 6.2.5 /8).
6.5 /5:
Wenn während der Auswertung eines Ausdrucks eine Ausnahmebedingung eintritt (d. h. wenn das Ergebnis nicht mathematisch definiert ist bzw nicht im Bereich darstellbarer Werte für seinen Typ), ist das Verhalten undefiniert.
Wenn Ihr `int` 32 Bit breit ist (oder weniger)
dann ist das Beispiel im OP ein Verstoß gegen Einschränkung 6.7.2.2 /2:
Der Ausdruck, der den Wert einer Aufzählungskonstante definiert, muss ein ganzzahliger konstanter Ausdruck sein, der einen als darstellbaren Wert hat int
.
Außerdem sind die Enumeratoren als Konstanten vom Typ definiert int
, 6.7.2.2 /3:
Die Bezeichner in einer Aufzählungsliste werden als Konstanten deklariert, die einen Typ haben int
und dürfen überall dort erscheinen, wo dies erlaubt ist.
Beachten Sie, dass es einen Unterschied zwischen dem gibt Art der Aufzählung und die Typ eines Enumerators / einer Enumerationskonstante:
enum foo { val0 };
enum foo myVariable; // myVariable has the type of the enumeration
uint_least8_t v = val0*'c'; // if val0 appears in any expression, it has type int
Es scheint mir, dass dies eine Verengung ermöglicht, z. B. die Reduzierung der Größe der Aufzählung Typ auf 8 Bit:
enum foo { val1 = 1, val2 = 5 };
enum foo myVariable = val1; // allowed to be 8-bit
Aber es scheint verbieten Verbreiterung, z
enum foo { val1 = INT_MAX+1 }; // constraint violation AND undefined behaviour
// not sure about the following, we're already in UB-land
enum foo myVariable = val1; // maximum value of an enumerator still is INT_MAX
// therefore myVariable will have sizeof int
Automatisches Inkrement von Enumeratoren
Wegen 6.7.2.2 /3,
[…] Jeder nachfolgende Zähler mit Nr =
definiert seine Aufzählungskonstante als den Wert von konstanter Ausdruck, der durch Addition von 1 erhalten wird auf den Wert der vorherigen Enumerationskonstante. […]
das Beispiel ergibt in UB:
enum foo {
val0 = INT_MAX,
val1 // equivalent to `val1 = INT_MAX+1`
};
Hier ist die C++-Antwort: In 7.2/6 heißt es:
[…] Der zugrunde liegende Typ ist ein ganzzahliger Typ, der alle in der Enumeration definierten Enumeratorwerte darstellen kann. Wenn kein ganzzahliger Typ alle Enumeratorwerte darstellen kann, ist die Enumeration falsch formatiert. Es ist implementierungsdefiniert, welcher ganzzahlige Typ als zugrunde liegender Typ verwendet wird, außer dass der zugrunde liegende Typ nicht größer als int sein darf, es sei denn, der Wert eines Enumerators passt nicht in ein int oder unsigned int.
Im Vergleich zu C: kein undefiniertes Verhalten, wenn der Compiler keinen Typ finden kann und der Compiler nicht einfach seinen erweiterten 512-Bit-Ganzzahltyp für Ihre zweiwertige Aufzählung verwenden kann.
Das bedeutet, dass in Ihrem Beispiel der zugrunde liegende Typ dies tut wahrscheinlich ein signierter 64-Bit-Typ sein – die meisten Compiler versuchen immer zuerst die signierte Version eines Typs.
Dies wird bestenfalls von der Implementierung definiert, die Größe einer Aufzählung ist nicht begrenzt.
– Kevin
11. Juni 2013 um 13:45 Uhr
Nur zum Spaß würden beide Ausdrücke kompilieren und sehr kleine Zahlen produzieren – 28 und 29.
– Sergej Kalinitschenko
11. Juni 2013 um 13:49 Uhr
@DyP Nein, es war vorher ein XOR-Operator – OP hat es verwendet, um “zwei hoch” zu veranschaulichen.
– Sergej Kalinitschenko
11. Juni 2013 um 14:01 Uhr
@Bathsheba: C und C++ sind unterschiedliche Sprachen; welches willst du?
– Nicol Bolas
11. Juni 2013 um 15:06 Uhr
@Bathsheba: Ich habe gesehen, dass du es als beides markiert hast. Das ist mein Punkt: die Antworten sind anders zwischen ihnen.
– Nicol Bolas
11. Juni 2013 um 15:14 Uhr