Gibt es Garantien für die Darstellung großer Aufzählungswerte?

Lesezeit: 6 Minuten

Benutzer-Avatar
Bathseba

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?

  • 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

Benutzer-Avatar
md5

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.

  • Ist es aber nicht uint_least64_t zwingend vorhanden sein? Ist es nicht ein vorzeichenloser ganzzahliger Typ?

    – dyp

    11. Juni 2013 um 13:52 Uhr


  • @Dyp, ja, damit der Compiler es auswählen kann. Allerdings, wenn Sie dies tun 0xFFFFFFFFFFFFFFFFllu dann wäre es ein undefiniertes Verhalten, wenn der Compiler keine ganzen Zahlen größer als 64 Bit unterstützt.

    – Shahbaz

    11. Juni 2013 um 14:01 Uhr


  • Ebenfalls, unsigned long long ist erforderlich, um speichern zu können wenigstens 2^64-1 (“hoch”, nicht “XOR”), daher sollte 0xFF FF FF FF kein Problem sein.

    – dyp

    11. Juni 2013 um 14:10 Uhr

  • Ich könnte mich irren, aber ich denke, es ist relevanter, dass C den Typ der Enumeratoren erfordert int (ausschließlich), 6.7.2.2

    – dyp

    11. Juni 2013 um 14:20 Uhr

  • @DyP hast du ein Zitat? Das obige Zitat verlangt nur einen “ganzzahligen Typ”, der lang sein kann, uint_least64_t oder andere.

    – Arne Mertz

    11. Juni 2013 um 14:23 Uhr

Benutzer-Avatar
Arne Merz

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.

  • “wenn es einen ganzzahligen Typ gibt, der größer als 32 Bit ist” Und es gibt, unsigned long long wird von C99 benötigt, um mindestens 2^64 -1 speichern zu können. Es gibt uint_least64_t auch.

    – dyp

    11. Juni 2013 um 14:22 Uhr

  • Auch hier denke ich, es ist relevanter, was die Typ des Zählers ist, nicht der Typ der Aufzählung; sehen [dcl.enum]/5. “Wenn der zugrunde liegende Typ nicht festgelegt ist, ist der Typ jedes Enumerators der Typ seines Initialisierungswerts”, aber er wächst gemäß Unterpunkt 3 (im Gegensatz zu den Anforderungen in C99/11)

    – dyp

    11. Juni 2013 um 14:56 Uhr


  • Ich denke, die Aufzählung ist ein Typ und nicht haben eine Art. Der Typ der Enumeratoren ist foo in C++, anders als in C. §7.2,1 des C++-Standards besagt eindeutig: “Eine Aufzählung ist ein eindeutiger Typ mit benannten Konstanten”

    – Arne Mertz

    11. Juni 2013 um 15:59 Uhr


  • Ja, die Aufzählung ist ein Typ, aber dasselbe gilt für C, 6.2.5 /16 “Jede eindeutige Aufzählung bildet einen anderen Aufzählungstyp.” Ich denke jedoch immer noch, dass es nicht relevant ist, ob die Aufzählung ein Typ ist und welchen zugrunde liegenden Typ sie hat, da das OP über Aufzählungen spricht.

    – dyp

    11. Juni 2013 um 16:26 Uhr


  • Vielleicht möchten Sie Folgendes hinzufügen: „Nach der schließenden Klammer von an enum-specifier, jeder Enumerator hat den Typ seiner Enumeration.” und einige der folgenden von [dcl.enum]/5

    – dyp

    11. Juni 2013 um 16:47 Uhr

Benutzer-Avatar
dyp

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`
};

  • int kann bis zu 16 Bit schmal sein.

    – Keith Thompson

    11. Juni 2013 um 15:41 Uhr

Benutzer-Avatar
Sebastian Redl

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.

1370690cookie-checkGibt es Garantien für die Darstellung großer Aufzählungswerte?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy