Enums y typedef
Enumeraciones con tipo subyacente fijo (novedad de C23), el arte de typedef para nombrar tipos, y los tipos opacos que ocultan implementación tras una API.
Nombrar bien los tipos es diseño, no cosmética. Los enums dan nombres a conjuntos de valores; typedef crea alias que aclaran la intención y habilitan la encapsulación. Con C23, los enums además ganan control sobre su representación en memoria.
- Enums y su valor numérico.
- Enums con tipo subyacente fijo (C23).
- typedef para claridad y abstracción.
- Tipos opacos: ocultar la implementación.
Enums
Un enum da nombres a constantes enteras relacionadas:
enum Estado {
INACTIVO, // 0
CARGANDO, // 1
LISTO, // 2
ERROR = 10, // puedes fijar valores
ABORTADO // 11 (sigue desde el anterior)
};
enum Estado s = LISTO;
Usa enums en vez de “números mágicos” (if (s == 2)): if (s == LISTO) se lee solo y el compilador te ayuda con los switch exhaustivos.
Enums con tipo fijo (C23)
Antes, el tipo entero que respaldaba un enum lo elegía el compilador, lo que causaba problemas de tamaño y ABI. C23 te deja fijarlo:
enum Color : unsigned char { // C23: respaldado por unsigned char (1 byte)
ROJO, VERDE, AZUL
};
Sin tipo fijo, el tamaño de un enum era impredecible entre compiladores y plataformas — un problema real cuando ese enum forma parte de un formato binario, un protocolo de red o una struct compartida entre módulos compilados por separado. Fijar : unsigned char o : uint32_t garantiza el tamaño y el layout exactos, haciendo el enum seguro para serializar y para cruzar fronteras de ABI (nivel 21). Es otro ejemplo del patrón de C23: darte control explícito donde antes había ambigüedad de implementación.
typedef: alias de tipos
typedef crea un nombre nuevo para un tipo existente. Dos usos principales:
// 1) claridad: nombres con significado
typedef uint32_t Id;
typedef struct Punto Punto; // usar 'Punto' en vez de 'struct Punto'
// 2) simplificar tipos complejos (punteros a función)
typedef int (*Operacion)(int, int);
Operacion op = sumar; // mucho más legible
typedef aclara, pero también puede ocultar. Un typedef sobre un puntero (typedef struct X *XPtr) esconde que hay una indirección, lo que confunde sobre la propiedad y la vida del objeto. La convención sensata: usa typedef para structs (evita repetir struct), para punteros a función (que son ilegibles sin él), y para tipos opacos (abajo); pero no escondas punteros normales tras un alias. Que el lector vea el * cuando lo hay.
Tipos opacos: encapsulación en C
El patrón más potente que habilita typedef: exponer un tipo en tu header sin revelar su contenido. El usuario recibe un puntero opaco y solo puede tocarlo a través de tu API.
// archivo.h (lo que ve el usuario)
typedef struct Conexion Conexion; // declaración adelantada, sin campos
Conexion *conexion_abrir(const char *host);
void conexion_cerrar(Conexion *c);
int conexion_enviar(Conexion *c, const char *msg);
// archivo.c (privado)
struct Conexion { // la definición real, oculta
int socket;
char buffer[4096];
// …
};
Este patrón es cómo C logra encapsulación de verdad — el equivalente a los campos privados de una clase. El usuario de tu biblioteca recibe un Conexion * que puede pasar por ahí, pero no puede acceder a sus campos ni depende de su tamaño: solo existe tu API pública. Esto te da libertad total para cambiar la implementación (añadir campos, reorganizar) sin romper a nadie, y es la base de una ABI estable (nivel 21). FILE * de la librería estándar es exactamente esto: nunca has visto qué hay dentro de un FILE, y no lo necesitas. Diseñar tus módulos con tipos opacos es programación de sistemas madura.
- Define un enum de estados y úsalo en un
switch(con-Wallpara el chequeo exhaustivo). - Crea un enum C23 con tipo subyacente fijo y comprueba su
sizeof. - Usa
typedefpara un puntero a función y simplifica una declaración. - Diseña un tipo opaco: header con la API,
.ccon la struct oculta. El usuario nunca ve los campos.