Menu

Enum en C: constantes con nombre, numeración automática y enum a string

Cómo definir un enum en C: numeración automática, valores explícitos, usar enums en un switch para que el compilador detecte casos faltantes, convertir un enum a cadena, y las convenciones de nombres que los mantienen legibles.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

El código lleno de números pelados es código que nadie puede leer. if (state == 2) no te dice nada; if (state == STATE_RUNNING) te lo dice todo. Un enum es la manera de C de darle nombres propios a un conjunto pequeño y fijo de enteros, y a diferencia de un montón de #define, vienen agrupados en un tipo que el compilador conoce.

Definir un enum

La declaración nombra una etiqueta y lista los enumeradores entre llaves:

Dos cosas que hay que notar de inmediato. Primero, la numeración empieza en 0 y aumenta de uno en uno, así que RED es 0, GREEN es 1 y BLUE es 2. Segundo, los nombres de los enumeradores son visibles en todo el alcance que los envuelve: escribes GREEN, no Color.GREEN. Viven en el mismo espacio de nombres que tus variables, que es por lo que el código C convencionalmente los GRITA o los prefija por tipo (COLOR_RED) para evitar colisiones.

Imprimir un enum usa %d, porque un valor de enumeración es un valor entero.

Valores explícitos

Asigna a cualquier enumerador una constante explícita, y los que siguen continúan desde ahí:

Los valores no tienen que ser únicos ni estar ordenados: dos enumeradores pueden compartir un número, lo que ocasionalmente es útil para alias (COLOR_DEFAULT = COLOR_BLACK). Deben ser constantes enteras de tiempo de compilación, así que no puedes calcular uno a partir de una variable.

Un modismo común usa esto para registrar la cantidad:

enum Suit {
    CLUBS, DIAMONDS, HEARTS, SPADES,
    SUIT_COUNT           /* automáticamente 4 - sigue siendo correcto si agregas un palo */
};

SUIT_COUNT no es un palo real; es cuántos hay, mantenido gratis por la numeración automática. Los bucles y los tamaños de arrays pueden usarlo y nunca quedar desactualizados.

typedef enum

Igual que con los structs, el nombre completo del tipo en C es enum Color, y un typedef elimina la palabra clave:

Como los enumeradores son enteros ordinarios que empiezan en 0, sirven también como índices de array: dx[DIR_EAST] funciona sin ninguna conversión. Esa es la propiedad más útil de un enum basado en cero.

Enums en un switch

Emparejar un enum con un switch es donde el tipo empieza a pagarse solo. Compila con -Wall y el compilador te dirá cuándo olvidaste un caso.

Agrega un STATE_FAILED al enum y GCC advierte: enumeration value 'STATE_FAILED' not handled in switch. Esa advertencia es lo más parecido que C tiene a una comprobación de exhaustividad, y es una razón fuerte para escribir estos switches sin un caso default:: un default silencia la advertencia y deja que los estados nuevos pasen sin manejar. Pon el respaldo después del switch en su lugar, como hace describe.

Convertir un enum a cadena

C no guarda nombres en tiempo de ejecución; printf("%s", GREEN) no puede funcionar porque GREEN es simplemente 1. Dos patrones lo resuelven.

La versión con switch, que el compilador verifica:

Y la versión con tabla de búsqueda, más corta pero sin verificar: si el array y el enum se separan, nada advierte.

Los índices designados del array ([RED] = "RED") al menos mantienen los nombres pegados a sus casillas, así que reordenar el enum no puede barajar las cadenas en silencio. La comprobación de límites importa porque nada impide que quien llama pase un valor fuera del enum: mira la sección siguiente.

Los enums no son herméticos

Una variable de enum es un entero por debajo, y C no vigila el rango:

La aritmética sobre enumeradores funciona, las comparaciones con ints simples funcionan, y una conversión fuera de rango se acepta. Así que trata un enum como excelente documentación y una buena pista para el compilador, no como una garantía. Valida cualquier valor que llegue de fuera de tu programa — un archivo, un argumento, un mensaje de red — antes de hacerle switch.

Tamaño y almacenamiento

En C17, todo enumerador debe caber en un int: enum Huge { BIG = 3000000000 }; es una violación de restricción que un compilador estricto (-pedantic-errors) rechaza, aunque gcc y clang la acepten como extensión en su modo por defecto. Los enumeradores en sí tienen tipo int, mientras que el tamaño del tipo enum está definido por la implementación (en la práctica, int):

(C23 levanta la restricción: un enumerador puede exceder el rango de int e incluso puedes elegir el tipo subyacente con enum E : unsigned long { ... }. Hasta que tu cadena de herramientas apunte a C23, mantén los enumeradores dentro de int.)

Como la elección exacta está definida por la implementación, nunca escribas un enum directamente en un archivo binario o un paquete de red y esperes que otra máquina lo lea de vuelta. Convierte a un tipo de ancho fijo como uint8_t o uint32_t de <stdint.h> en la frontera.

Enum frente a #define

Ambos le dan un nombre a un número. El enum es generalmente mejor:

enum#define
Lo manejael compiladorel preprocesador
Agrupaciónlos valores relacionados comparten un tipocada constante es independiente
Numeración automáticano, numeras a mano
Visible en un depuradora menudo, por nombreno, solo el número
Advertencia de exhaustividad en switchno
Alcancerespeta el alcance de bloquetextual, hasta #undef

Recurre a #define cuando la constante no sea un entero (una cadena, un float) o cuando deba ser utilizable por el propio preprocesador, como en compilación condicional. Para un conjunto de estados, banderas o clases enteras relacionadas, usa un enum.

Convenciones de nombres

Los enums chocan con facilidad porque sus nombres viven en el alcance ordinario. Las convenciones que mantienen cuerdas a las bases de código grandes de C:

  • Prefija cada enumerador con el tipo: COLOR_RED, STATE_IDLE, HTTP_OK. Un RED sin prefijo en un programa de gráficos terminará encontrándose con el RED de alguien más.
  • Escribe los enumeradores en MAYUSCULAS, igual que se ven las demás constantes.
  • Nombra el tipo en PascalCase singular o snake_case_t: Color, State o color_t, de forma consistente en todo el proyecto.
  • Agrega un centinela _COUNT al final cuando vayas a iterar o indexar con el enum.

Preguntas frecuentes

¿Cómo se define un enum en C?

enum Color { RED, GREEN, BLUE }; define un tipo cuyos valores son las tres constantes con nombre. Por defecto se numeran desde 0, así que RED es 0, GREEN es 1 y BLUE es 2. Declara una variable con enum Color c = GREEN;, o agrega un typedef para poder escribir solo Color c.

¿Se pueden asignar valores específicos a un enum en C?

Sí: enum Status { OK = 200, NOT_FOUND = 404, ERROR = 500 };. Puedes fijar algunos y dejar que los otros sigan: cualquier nombre sin valor explícito continúa desde el anterior, así que enum { A = 10, B, C }; da B = 11 y C = 12.

¿Cómo se convierte un enum a cadena en C?

C no tiene una forma integrada; los nombres de los enumeradores no existen en tiempo de ejecución. El enfoque habitual es una función pequeña con un switch que devuelve un literal de cadena por valor, o un array de cadenas indexado por el enum. La versión con switch es más segura porque -Wall advierte cuando un enumerador nuevo no tiene caso.

¿Cuál es el tamaño de un enum en C?

Definido por la implementación, pero en la práctica sizeof(int): 4 bytes en la mayoría de los sistemas. El compilador elige un tipo entero capaz de contener a todos los enumeradores. No asumas un tamaño específico al armar un archivo binario o un paquete de red; usa ahí un tipo de ancho fijo de <stdint.h>.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR