Especificando el tamaño del tipo de enumeración en C

10 minutos de lectura

avatar de usuario
Voluntad

Ya leí esta pregunta relacionada, pero estaba buscando algo un poco más específico.

  • ¿Hay alguna manera de decirle a su compilador específicamente qué tan ancho quiere que sea su enumeración?
  • Si es así, como lo haces? Sé cómo especificarlo en C#; ¿Se hace de manera similar en C?
  • ¿Valdría la pena hacerlo? Cuando el valor de enumeración se pasa a una función, ¿se pasará como un int-valor de tamaño independientemente?

Creo que hay una bandera si está usando GCC.

-f-enumeraciones cortas

  • +1 Genial … eso es lo que estaba buscando en cuanto a forzar el compilador.

    – Voluntad

    2 de febrero de 2011 a las 20:14

  • o selectivamente enum __attribute__ ((__packed__)) my_enum {...};

    – Nikolái Fetissov

    2 de febrero de 2011 a las 20:16

  • @Nikolai N Fetissov: también lo intentaré. Sabía que podrías usar el __packed__ atributo en un struct; no sabía que también podría usarlo en un enum.

    – Voluntad

    2 de febrero de 2011 a las 20:18

  • Aquí hay un poco más de información. stackoverflow.com/a/54527229/4561887

    – Gabriel grapas

    5 de febrero de 2019 a las 3:11

avatar de usuario
Hormiga

¿Hay alguna manera de decirle a su compilador específicamente qué tan ancho quiere que sea su enumeración?

En general el caso núm. No en estándar C.

¿Valdría la pena hacerlo?

Depende del contexto. Si está hablando de pasar parámetros a funciones, entonces no, no vale la pena hacerlo (ver más abajo). Si se trata de ahorrar memoria al crear agregados a partir de tipos de enumeración, entonces podría valer la pena hacerlo. Sin embargo, en C simplemente puede usar un tipo de entero de tamaño adecuado en lugar del tipo de enumeración en los agregados. En C (a diferencia de C++), los tipos de enumeración y los tipos enteros casi siempre son intercambiables.

Cuando el valor de enumeración se pasa a una función, ¿se pasará como un valor de tamaño int independientemente?

Muchos (la mayoría) de los compiladores en estos días pasan todos los parámetros como valores de tamaño de palabra natural para la plataforma de hardware dada. Por ejemplo, en una plataforma de 64 bits, muchos compiladores pasarán todos los parámetros como valores de 64 bits, independientemente de su tamaño real, incluso si el tipo int tiene 32 bits en esa plataforma (por lo tanto, generalmente no se pasa como valor “int-size” en dicha plataforma). Por esta razón, no tiene sentido intentar optimizar los tamaños de enumeración para pasar parámetros.

  • “Si está hablando de pasar parámetros a funciones, entonces no, no vale la pena hacerlo (ver más abajo)” No siempre. Si usa un procesador de 8 bits, el tamaño natural es de 8 bits, pero un int y una enumeración tienen al menos 16 bits (sin -fshort-enums), por lo que necesita al menos 2 registros que puedan ralentizar las llamadas a funciones.

    – 12431234123412341234123

    2 de septiembre de 2016 a las 9:22

avatar de usuario
cristobal johnson

Puede obligarlo a tener al menos un cierto tamaño definiendo un valor apropiado. Por ejemplo, si desea que su enumeración se almacene con el mismo tamaño que un intaunque todos los valores cabrían en un charpuedes hacer algo como esto:

typedef enum {
    firstValue = 1,
    secondValue = 2,

    Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;

Tenga en cuenta, sin embargo, que el comportamiento puede depender de la implementación.

Como observa, pasar dicho valor a una función hará que se expanda a un int de todos modos, pero si está usando su tipo en una matriz o estructura, entonces el tamaño importará. Si realmente te importan los tamaños de los elementos, deberías usar tipos como int8_t, int32_tetc

  • Eso es algo de lo que pensé. Tiendo a tener una cantidad de enumeraciones definidas que realmente no necesitan más de 8 o 16 bits, y odio desperdiciar espacio cuando no es necesario.

    – Voluntad

    2 de febrero de 2011 a las 20:16

  • Esto podría no funcionar si el optimizador elimina su no utilizado Internal_ForceMyEnumIntSize

    – EBlake

    25 de enero de 2017 a las 23:47

  • @KJohnson ¿Internal_ForceMyEnumIntSize es una palabra clave C?

    – Patricio

    19 de marzo de 2017 a las 6:42

  • @Patrick No, es solo otro miembro de la enumeración. Podría haberse llamado tercer valor, último valor o valor máximo. MAX_INT está definido por C como el valor int máximo.

    –Kristopher Johnson

    20 de marzo de 2017 a las 11:33

  • @EBlake C99 dice que el tipo “deberá ser capaz de representar los valores de todos los miembros de la enumeración”. No dice nada acerca de permitir que un optimizador elimine miembros, por lo que no puedo ver cómo se permitiría eso. ¿Tampoco puedo ver cuál sería el punto de hacerlo? ¿Qué hay que ganar? Los enumeradores son solo formas alternativas de nombrar constantes integrales; si un miembro del enumerador no se usa en el código, seguramente no puede aparecer en el binario, por lo que no hay nada que eliminar, nada que optimizar.

    – subrayado_d

    16 de septiembre de 2018 a las 16:24

En algunas circunstancias, esto puede ser útil:

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; 

En un tipo de mano command_t tiene tamaño 8 y se puede utilizar para variables y tipos de parámetros de función. Por otro lado, puede usar los valores de enumeración para la asignación que son del tipo int por defecto, pero el compilador los emitirá inmediatamente cuando se asigne a un command_t variable tipo.

Además, si hace algo inseguro como definir y usar un CMD_16bit = 0xFFFF, el compilador le advertirá con el siguiente mensaje:

advertencia: entero grande implícitamente truncado a tipo sin firmar [-Woverflow]

avatar de usuario
patricio schlüter

También hay otra forma si la enumeración es parte de una estructura:

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

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

El :0; se puede omitir si el campo de enumeración está rodeado de campos normales. Si hay otro campo de bits antes, el :0 forzará la alineación de bytes al siguiente byte para el campo que le sigue.

  • Ni remotamente; ninguna permutación de banderas que pude encontrar en gcc lo compilaría, solo dando error: expected specifier-qualifier-list before ‘:’ token. Los campos de bits de tamaño cero son una cosa, y deben estar sin nombre, pero aún deben tener un tipo. Supongo que un campo de bits de tamaño cero/sin nombre significa ‘iniciar el siguiente campo de bits de tamaño distinto de cero al comienzo de la siguiente unidad de asignación’, donde su tipo especifica a qué unidad alinearse. Supongo que eso abre la pregunta de por qué necesita un tipo, cuando el siguiente campo de bits de tamaño distinto de cero tiene uno, pero es posible que necesite una máquina del tiempo para preguntarle al Comité que

    – subrayado_d

    16/09/2018 a las 16:38

  • Esto debe marcarse como la solución correcta. Todas las demás respuestas se pueden optimizar, mientras que se garantiza el uso de un campo de bits.

    – Twifty

    19 de diciembre de 2019 a las 6:46

  • No creo que esto se pueda usar para definir una enumeración que se pueda usar fuera de una estructura, ¿correcto?

    – diqueag

    20 de enero de 2021 a las 14:46

  • No. Solo dentro de un struct ya que es una propiedad de la sintaxis del conjunto de bits.

    – Patrick Schlüter

    20 de enero de 2021 a las 15:17

Incluso si está escribiendo estrictamente C código, los resultados van a depender del compilador. Empleando las estrategias de este hilo, obtuve algunos resultados interesantes…

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

En mi ejemplo anterior, no me di cuenta de ningún beneficio de __attribute__((__packed__)) modificador hasta que comencé a usar el compilador de C++.

EDITAR:

La sospecha de @technosaurus era correcta.

Al comprobar el tamaño de sizeof(enum PackedFlags) en lugar de sizeof(PACKED) Veo los resultados que esperaba…

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

Ahora veo los resultados esperados de gcc:

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

  • Ni remotamente; ninguna permutación de banderas que pude encontrar en gcc lo compilaría, solo dando error: expected specifier-qualifier-list before ‘:’ token. Los campos de bits de tamaño cero son una cosa, y deben estar sin nombre, pero aún deben tener un tipo. Supongo que un campo de bits de tamaño cero/sin nombre significa ‘iniciar el siguiente campo de bits de tamaño distinto de cero al comienzo de la siguiente unidad de asignación’, donde su tipo especifica a qué unidad alinearse. Supongo que eso abre la pregunta de por qué necesita un tipo, cuando el siguiente campo de bits de tamaño distinto de cero tiene uno, pero es posible que necesite una máquina del tiempo para preguntarle al Comité que

    – subrayado_d

    16/09/2018 a las 16:38

  • Esto debe marcarse como la solución correcta. Todas las demás respuestas se pueden optimizar, mientras que se garantiza el uso de un campo de bits.

    – Twifty

    19 de diciembre de 2019 a las 6:46

  • No creo que esto se pueda usar para definir una enumeración que se pueda usar fuera de una estructura, ¿correcto?

    – diqueag

    20 de enero de 2021 a las 14:46

  • No. Solo dentro de un struct ya que es una propiedad de la sintaxis del conjunto de bits.

    – Patrick Schlüter

    20 de enero de 2021 a las 15:17

Como dice @Nyx0uf, GCC tiene un indicador que puede configurar:

-fshort-enums

Asigne a un tipo de enumeración solo tantos bytes como necesite para el rango declarado de valores posibles. Específicamente, el tipo de enumeración es equivalente al tipo de entero más pequeño que tiene suficiente espacio.

Advertencia: el -fshort-enums switch hace que GCC genere un código que no es compatible binario con el código generado sin ese switch. Úselo para ajustarse a una interfaz binaria de aplicación no predeterminada.

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

Gran lectura adicional para una visión general: https://www.embedded.fm/blog/2016/6/28/how-big-is-an-enum.
Interesante… ¡fíjate en la línea que destaqué en amarillo a continuación! Agregar una entrada de enumeración llamada ARM_EXCEPTION_MAKE_ENUM_32_BIT y con un valor igual a 0xffffffffque es el equivalente de UINT32_MAX desde stdint.h (ver aquí y aquí), obliga a este particular Arm_symbolic_exception_name enum para tener un tipo entero de uint32_t. Ese es el único propósito de este ARM_EXCEPTION_MAKE_ENUM_32_BIT ¡entrada! funciona porque uint32_t es el tipo entero más pequeño que puede contener todos los valores de enumeración en esta enumeración, a saber: 0 a través de 8inclusive, así como 0xffffffffo decimal 2^32-1 = 4294967295.

ingrese la descripción de la imagen aquí

Palabras clave: ARM_EXCEPTION_MAKE_ENUM_32_BIT propósito de enumeración ¿por qué tenerlo? Arm_symbolic_exception_name Propósito de la entrada de enumeración 0xffffffff al final.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad