Angabe der Größe des Aufzählungstyps in C

Lesezeit: 8 Minuten

Wills Benutzeravatar
Werden

Ich habe diese verwandte Frage bereits durchgelesen, suchte aber nach etwas Spezifischerem.

  • Gibt es eine Möglichkeit, Ihrem Compiler genau mitzuteilen, wie breit Ihre Aufzählung sein soll?
  • Wenn ja, wie machst du das? Ich weiß, wie man es in C# angibt; ist es ähnlich in C gemacht?
  • Würde es sich überhaupt lohnen? Wenn der Aufzählungswert an eine Funktion übergeben wird, wird er als übergeben int-großer Wert unabhängig?

Ich glaube, es gibt ein Flag, wenn Sie GCC verwenden.

-fshort-enums

  • +1 Cool … danach habe ich gesucht, um den Compiler zu erzwingen.

    – Werden

    2. Februar 2011 um 20:14 Uhr

  • Oder selektiv enum __attribute__ ((__packed__)) my_enum {...};

    – Nikolai Fetissov

    2. Februar 2011 um 20:16 Uhr

  • @Nikolai N Fetissov: werde das auch mal ausprobieren. Ich wusste, dass du das gebrauchen kannst __packed__ Attribut auf a struct; wusste nicht, dass man es auch an einem verwenden könnte enum.

    – Werden

    2. Februar 2011 um 20:18 Uhr

  • Hier noch ein wenig mehr Einblick. stackoverflow.com/a/54527229/4561887

    – Gabriel Staples

    5. Februar 2019 um 3:11 Uhr

  • Wie die GCC-Dokumentation sagt, mit dem Flag -fshort-enums ist gefährlich, da es wirkt alle Enum-Typen. Besser verwenden __attribute__((__packed__)).

    – Roland Illig

    22. Mai um 10:09 Uhr

AnT steht mit Russlands Benutzer-Avatar
AnT steht zu Russland

Gibt es eine Möglichkeit, Ihrem Compiler genau mitzuteilen, wie breit Ihre Aufzählung sein soll?

Im Allgemeinen Fall Nr. Nicht in Standard-C.

Würde es sich überhaupt lohnen?

Es kommt auf den Kontext an. Wenn Sie über die Übergabe von Parametern an Funktionen sprechen, dann nein, es lohnt sich nicht (siehe unten). Wenn es darum geht, beim Erstellen von Aggregaten aus Enum-Typen Speicher zu sparen, kann es sich lohnen, dies zu tun. In C können Sie jedoch in Aggregaten einfach einen Integer-Typ geeigneter Größe anstelle eines Enum-Typs verwenden. In C (im Gegensatz zu C++) sind Enum-Typen und Integer-Typen fast immer austauschbar.

Wenn der Enum-Wert an eine Funktion übergeben wird, wird er trotzdem als Int-Size-Wert übergeben?

Viele (die meisten) Compiler übergeben heutzutage alle Parameter als Werte der natürlichen Wortgröße für die jeweilige Hardwareplattform. Beispielsweise übergeben viele Compiler auf einer 64-Bit-Plattform alle Parameter als 64-Bit-Werte, unabhängig von ihrer tatsächlichen Größe, selbst wenn Typ int hat auf dieser Plattform 32 Bits (also wird es auf einer solchen Plattform im Allgemeinen nicht als “int-sized” Wert übergeben). Aus diesem Grund macht es keinen Sinn, Enum-Größen für Parameterübergabezwecke zu optimieren.

  • “Wenn Sie davon sprechen, Parameter an Funktionen zu übergeben, dann lohnt es sich nicht (siehe unten)” Nicht immer. Wenn Sie einen 8-Bit-Prozessor verwenden, beträgt die natürliche Größe 8 Bit, aber ein int und enum sind mindestens 16 Bit (ohne -fshort-enums), sodass Sie dort mindestens 2 Register benötigen, die Funktionsaufrufe verlangsamen können.

    – 12431234123412341234123

    2. September 2016 um 9:22 Uhr

Benutzeravatar von Kristopher Johnson
Christoph Johnson

Sie können eine bestimmte Größe erzwingen, indem Sie einen entsprechenden Wert definieren. Wenn Sie beispielsweise möchten, dass Ihre Aufzählung in derselben Größe wie eine gespeichert wird intobwohl alle Werte in a passen würden charkönnen Sie so etwas tun:

typedef enum {
    firstValue = 1,
    secondValue = 2,

    Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;

Beachten Sie jedoch, dass das Verhalten von der Implementierung abhängig sein kann.

Wie Sie bemerken, wird die Übergabe eines solchen Werts an eine Funktion ohnehin zu einem int erweitert, aber wenn Sie Ihren Typ in einem Array oder einer Struktur verwenden, spielt die Größe eine Rolle. Wenn Sie sich wirklich für Elementgrößen interessieren, sollten Sie wirklich Typen wie verwenden int8_t, int32_tetc.

  • So ungefähr dachte ich. Ich neige dazu, eine Reihe von Aufzählungen definiert zu haben, die wirklich nicht mehr als 8 oder 16 Bit benötigen, und ich hasse es, Platz zu verschwenden, wenn ich es nicht muss.

    – Werden

    2. Februar 2011 um 20:16 Uhr

  • Dies funktioniert möglicherweise nicht, wenn der Optimierer Ihre nicht verwendeten entfernt Internal_ForceMyEnumIntSize

    – EBlake

    25. Januar 2017 um 23:47 Uhr

  • @KJohnson Ist Internal_ForceMyEnumIntSize ein C-Schlüsselwort?

    – Patrick

    19. März 2017 um 6:42 Uhr

  • @Patrick Nein, es ist nur ein weiteres Mitglied der Aufzählung. Es hätte auch ThirdValue, LastValue oder MaxValue heißen können. MAX_INT wird von C als der maximale int-Wert definiert.

    – Christoph Johnson

    20. März 2017 um 11:33 Uhr

  • @EBlake C99 sagt, dass der Typ “in der Lage sein muss, die Werte aller Mitglieder der Aufzählung darzustellen”. Es sagt nichts darüber aus, einem Optimierer zu erlauben, Mitglieder zu entfernen, also kann ich nicht sehen, wie das erlaubt wäre. Ich kann auch nicht sehen, was der Sinn wäre, es zu tun? Was gibt es zu gewinnen? Enumeratoren sind nur alternative Möglichkeiten, ganzzahlige Konstanten zu benennen; Wenn ein Enumerator-Member nicht im Code verwendet wird, kann es sicherlich nicht in der Binärdatei erscheinen, also gibt es nichts zu entfernen, nichts zu optimieren.

    – Unterstrich_d

    16. September 2018 um 16:24 Uhr

Benutzeravatar von Patrick Schlüter
Patrick Schlüter

Es gibt auch einen anderen Weg, wenn die Aufzählung Teil einer Struktur ist:

enum whatever { a,b,c,d };

struct something {
   char  :0;
   enum whatever field:CHAR_BIT;
   char  :0;
};

Das :0; kann weggelassen werden, wenn das Aufzählungsfeld von normalen Feldern umgeben ist. Wenn vorher ein anderes Bitfeld vorhanden ist, erzwingt :0 die Byte-Ausrichtung auf das nächste Byte für das darauf folgende Feld.

Juliens Benutzeravatar
Julien

Unter Umständen kann dies hilfreich sein:

typedef uint8_t command_t;
enum command_enum
{
    CMD_IDENT                = 0x00,     //!< Identify command
    CMD_SCENE_0              = 0x10,     //!< Recall Scene 0 command
    CMD_SCENE_1              = 0x11,     //!< Recall Scene 1 command
    CMD_SCENE_2              = 0x12,     //!< Recall Scene 2 command
};

/* cmdVariable is of size 8 */
command_t cmdVariable = CMD_IDENT; 

Einerseits Typ command_t hat die Größe 1 (8 Bit) und kann für Variablen- und Funktionsparametertypen verwendet werden. Andererseits können Sie die Enum-Werte für Zuweisungen verwenden, die vom Typ sind int standardmäßig, aber der Compiler wird sie sofort umwandeln, wenn sie a zugewiesen werden command_t Variable eingeben.

Auch wenn Sie etwas Unsicheres tun, wie z. B. das Definieren und Verwenden von a CMD_16bit = 0xFFFF, Der Compiler wird Sie mit folgender Meldung warnen:

Warnung: Große Ganzzahl wird implizit auf einen unsigned-Typ gekürzt [-Woverflow]

  • “der Compiler” bezieht sich hier auf GCC, andere Compiler können diese oder eine ähnliche Warnung ausgeben oder auch nicht.

    – Roland Illig

    22. Mai um 10:04 Uhr

  • Eigentlich wird dies von der Größe sein 1, weil sizeof(uint8_t) == 1 wo es überhaupt existiert. Es sind aber 8 Bit.

    – SamB

    1. September um 15:46 Uhr

Auch wenn Sie streng schreiben C code, werden die Ergebnisse vom Compiler abhängig sein. Mit den Strategien aus diesem Thread habe ich einige interessante Ergebnisse erhalten …

enum_size.c

#include <stdio.h>

enum __attribute__((__packed__)) PackedFlags {
    PACKED = 0b00000001,
};

enum UnpackedFlags {
    UNPACKED = 0b00000001,
};

int main (int argc, char * argv[]) {
  printf("packed:\t\t%lu\n", sizeof(PACKED));
  printf("unpacked:\t%lu\n", sizeof(UNPACKED));
  return 0;
}
$ gcc enum_size.c
$ ./a.out
packed:         4
unpacked:       4
$ gcc enum_size.c -fshort_enums
$ ./a.out
packed:         4
unpacked:       4
$ g++ enum_size.c
$ ./a.out
packed:         1
unpacked:       4
$ g++ enum_size.c -fshort_enums
$ ./a.out
packed:         1
unpacked:       1

In meinem obigen Beispiel habe ich keinen Nutzen davon erkannt __attribute__((__packed__)) Modifikator, bis ich anfing, den C++-Compiler zu verwenden.

BEARBEITEN:

Der Verdacht von @technosaurus war richtig.

Durch Überprüfung der Größe von sizeof(enum PackedFlags) Anstatt von sizeof(PACKED) Ich sehe die Ergebnisse, die ich erwartet hatte …

  printf("packed:\t\t%lu\n", sizeof(enum PackedFlags));
  printf("unpacked:\t%lu\n", sizeof(enum UnpackedFlags));

Ich sehe jetzt die erwarteten Ergebnisse aus gcc:

$ gcc enum_size.c
$ ./a.out
packed:         1
unpacked:       4
$ gcc enum_size.c -fshort_enums
$ ./a.out
packed:         1
unpacked:       1

  • “der Compiler” bezieht sich hier auf GCC, andere Compiler können diese oder eine ähnliche Warnung ausgeben oder auch nicht.

    – Roland Illig

    22. Mai um 10:04 Uhr

  • Eigentlich wird dies von der Größe sein 1, weil sizeof(uint8_t) == 1 wo es überhaupt existiert. Es sind aber 8 Bit.

    – SamB

    1. September um 15:46 Uhr

Wie @Nyx0uf sagt, hat GCC ein Flag, das Sie setzen können:

-fshort-enums

Ordnen Sie einem Aufzählungstyp nur so viele Bytes zu, wie er für den deklarierten Bereich möglicher Werte benötigt. Insbesondere entspricht der Aufzählungstyp dem kleinsten ganzzahligen Typ, der genügend Platz hat.

Achtung: die -fshort-enums switch bewirkt, dass GCC Code generiert, der nicht binärkompatibel mit Code ist, der ohne diesen Schalter generiert wurde. Verwenden Sie es, um sich an eine nicht standardmäßige binäre Anwendungsschnittstelle anzupassen.

Quelle: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html

Zusätzliche großartige Lektüre für allgemeine Einblicke: https://www.embedded.fm/blog/2016/6/28/how-big-is-an-enum.
Interessant … beachten Sie die Zeile, die ich unten gelb hervorgehoben habe! Hinzufügen eines Enum-Eintrags namens ARM_EXCEPTION_MAKE_ENUM_32_BIT und mit einem Wert gleich 0xffffffffwas das Äquivalent von ist UINT32_MAX aus stdint.h (sehen hier und hier), erzwingt diese besondere Arm_symbolic_exception_name enum einen ganzzahligen Typ von haben uint32_t. Das ist der einzige Zweck davon ARM_EXCEPTION_MAKE_ENUM_32_BIT Eintrag! Es funktioniert, weil uint32_t ist der kleinste ganzzahlige Typ, der alle Aufzählungswerte in dieser Aufzählung enthalten kann – nämlich: 0 durch 8einschließlich, sowie 0xffffffffoder dezimal 2^32-1 = 4294967295.

Geben Sie hier die Bildbeschreibung ein

Schlüsselwörter: ARM_EXCEPTION_MAKE_ENUM_32_BIT enum Zweck warum haben Sie es? Arm_symbolic_exception_name Zweck des 0xffffffff-Enum-Eintrags am Ende.

1408440cookie-checkAngabe der Größe des Aufzählungstyps in C

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

Privacy policy