Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Definición de Tipos y Alias

Typedef y enum en C

Universidad Nacional de Rio Negro - Sede Andina

Alias de Tipos con typedef

En C, el mecanismo nativo para crear nombres alternativos o alias de tipos de datos es la palabra clave typedef. A diferencia de lo que ocurre en otros lenguajes, typedef no introduce un nuevo tipo de dato físicamente diferente para el compilador; simplemente asocia un identificador secundario a un tipo existente (primitivo, puntero, estructura o enumeración) para simplificar la escritura o mejorar la abstracción conceptual del código.

Sintaxis Básica

La declaración se asemeja a la de una variable estándar, pero precedida por typedef:

typedef tipo_existente nuevo_nombre_t;

Por ejemplo, si necesitás trabajar con enteros que representen distancias en metros y querés que el código exprese con claridad esa unidad:

typedef double metros_t;

// Ahora podés usar 'metros_t' como un tipo estándar
metros_t distancia_casa = 1500.50;
metros_t altura_edificio = 45.2;

Físicamente, distancia_casa es un double. El compilador simplemente reemplazará sintácticamente metros_t por double durante el análisis semántico.

Abstracción de Plataforma

Uno de los usos más rigurosos de typedef es garantizar la portabilidad e independencia del hardware. Tipos de datos como int o long pueden variar su tamaño en bytes dependiendo de la arquitectura de la CPU (16, 32 o 64 bits).

Mediante typedef, se pueden definir alias que denoten explícitamente el ancho físico del tipo de dato, facilitando la compilación del mismo código en múltiples plataformas:

// Definiciones basadas en la arquitectura del compilador
typedef signed char        entero8_t;
typedef short int          entero16_t;
typedef int                entero32_t;
typedef long long int      entero64_t;

(Nota: En el C estándar moderno, estas definiciones ya se encuentran normalizadas en la cabecera estándar <stdint.h> mediante los tipos int8_t, int16_t, int32_t e int64_t).

Regla de Estilo de la Cátedra: El sufijo _t

Para mantener la claridad y coherencia en el código desarrollado, la cátedra impone la regla Regla 0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t, la cual establece que todo alias de tipo creado mediante typedef debe finalizar de forma obligatoria con el sufijo _t (por ejemplo, metros_t, velocidad_t, nodo_t). Esto permite distinguir instantáneamente los tipos personalizados de las variables y constantes en cualquier bloque de código.

A lo largo de este apunte le iremos dando uso a este concepto de manera gradual, particularmente para simplificar la declaración de estructuras complejas (Capítulo Estructuras y Tipos Compuestos) y la definición de Tipos de Datos Abstractos (Capítulo Tipos de Datos Abstractos, Pilas y Colas).


Enumeraciones en C

Las enumeraciones (enum) constituyen un mecanismo fundamental en el lenguaje C para la definición de tipos de datos que representan un conjunto finito y discreto de valores con nombres simbólicos. A diferencia de usar valores literales o constantes dispersas en el código, las enumeraciones proporcionan una abstracción semántica que mejora considerablemente la legibilidad, mantenibilidad y robustez del programa.

Desde una perspectiva técnica, las enumeraciones son esencialmente constantes enteras nombradas que el compilador traduce en tiempo de compilación. Sin embargo, permiten modelar de forma explícita conjuntos limitados de valores, estados o categorías.

enum vs const vs #define

Si este concepto resulta similar a las constantes const y a los literales de preprocesador #define estás en lo cierto y todas las consideraciones de uso aplican para cualquiera de los tres conceptos.

Concepto de enumeraciones vs números mágicos

Figure 1:Las enumeraciones proporcionan nombres simbólicos a valores enteros, transformando números mágicos en código legible y mantenible.

Aunque es posible definir constantes enteras con #define o una serie de variables const, el uso de enum es la práctica superior y más segura para agrupar constantes relacionadas. A diferencia de las macros de preprocesador (#define), que carecen de tipo y pueden contaminar el espacio de nombres global, enum crea un tipo de dato distinto. Esto mejora la seguridad de tipos y permite que el compilador detecte errores lógicos, como intentar comparar un día de la semana con un color. Además, los enum auto-numeran sus miembros secuencialmente, reduciendo la posibilidad de errores manuales y simplificando el mantenimiento. Esta agrupación lógica no solo hace el código más legible y auto-documentado, sino que también es reconocida por los depuradores, que pueden mostrar los nombres de las constantes (LUNES, MARTES) en lugar de sus valores numéricos (0, 1), facilitando enormemente la depuración.

Sintaxis y Declaración

Declaración Básica

La sintaxis fundamental para declarar una enumeración sigue el patrón estándar de C para tipos definidos por el usuario:

enum nombre_enumeracion {
    IDENTIFICADOR_1,
    IDENTIFICADOR_2,
    IDENTIFICADOR_3,
    // ... más identificadores
};

Sintaxis básica de enumeración

Ejemplo

1
2
3
4
5
6
7
8
9
enum estado_conexion {
    DESCONECTADO,
    CONECTANDO,
    CONECTADO,
    ERROR_CONEXION
};

// Uso en variables
enum estado_conexion estado_actual = DESCONECTADO;

Definición de estados de conexión

Alcance y Namespaces

Concepto de Namespace

En el contexto de la programación, un namespace o espacio de nombres (ver término en el Glosario) es una región del código donde un conjunto de identificadores es visible y accesible sin ambigüedad. Es un mecanismo fundamental para organizar y separar lógicamente los nombres, evitando colisiones entre identificadores que de otro modo compartirían el mismo nombre.

Formalmente, un namespace define un contexto de resolución de nombres: cuando el compilador encuentra un identificador, debe determinar a qué entidad se refiere consultando el namespace activo. En C, este concepto está implícito en el sistema de alcances (scope), pero no existe un mecanismo explícito de namespaces como en lenguajes posteriores (C++, Java, Rust).

En C, las constantes de enumeración se ubican en el namespace global de identificadores ordinarios, compartiendo este espacio con nombres de variables, funciones y otros símbolos. Esto significa que una constante APAGADO de un enum entra en conflicto con cualquier otra variable, función o constante de enumeración que se llame APAGADO en el mismo ámbito de compilación.

La contaminación del namespace se refiere al problema de introducir demasiados identificadores en un mismo ámbito, incrementando la probabilidad de conflictos por nombres duplicados o similares. Este problema es particularmente relevante en enumeraciones porque cada constante introduce un identificador independiente en el namespace, a diferencia de los miembros de una struct que están contenidos dentro del namespace de la estructura.

Problema de Contaminación del Namespace

Un aspecto importante a considerar es que los identificadores de las constantes de enumeración se encuentran en el namespace global (en C89/C90) o en el namespace del ámbito donde se declara la enumeración (en estándares más recientes).

1
2
3
4
5
6
7
8
9
10
11
enum estado_motor {
    APAGADO,
    ENCENDIDO,
    ERROR
};

enum estado_luz {
    APAGADO,    // Error: redefinición de APAGADO
    PRENDIDO,
    PARPADEANDO
};

Conflictos potenciales de nombres

Solución con Prefijos

Una práctica recomendada es usar prefijos consistentes para evitar colisiones:

1
2
3
4
5
6
7
8
9
10
11
enum motor_estado {
    MOTOR_APAGADO,
    MOTOR_ENCENDIDO,
    MOTOR_ERROR
};

enum luz_estado {
    LUZ_APAGADA,
    LUZ_PRENDIDA, 
    LUZ_PARPADEANDO
};

Uso de prefijos para evitar conflictos

Asignación de Valores

Asignación Automática

Por defecto, se asignan valores enteros consecutivos comenzando desde 0:

enum dias_semana {
    LUNES,      // 0
    MARTES,     // 1
    MIERCOLES,  // 2
    JUEVES,     // 3
    VIERNES,    // 4
    SABADO,     // 5
    DOMINGO     // 6
};

Asignación automática de valores

Asignación Explícita

Podés especificar valores explícitos para cualquier constante. Los valores no especificados continúan la secuencia desde el último valor asignado:

1
2
3
4
5
6
7
8
enum codigo_error {
    EXITO = 0,
    ERROR_ARCHIVO = 100,
    ERROR_MEMORIA,          // 101
    ERROR_PERMISOS,         // 102
    ERROR_CRITICO = 500,
    ERROR_FATAL             // 501
};

Asignación mixta de valores

Uso de Expresiones Constantes

Las asignaciones pueden utilizar expresiones constantes evaluables en tiempo de compilación:

#define BASE_ERROR 1000

enum errores_sistema {
    ERROR_KERNEL = BASE_ERROR,
    ERROR_DRIVER = BASE_ERROR + 50,
    ERROR_HARDWARE = BASE_ERROR + 100,
    ERROR_FIRMWARE = ERROR_HARDWARE + 10
};

Uso de expresiones constantes en enums

Definición de Tipos con typedef

Para mejorar la legibilidad, es recomendable usar typedef, a continuación en este mismo apunte, veremos otros usos típicos de esta expresión.

1
2
3
4
5
6
7
8
9
10
11
12
typedef enum {
    OPERACION_SUMA,
    OPERACION_RESTA,
    OPERACION_MULTIPLICACION,
    OPERACION_DIVISION
} operacion_matematica_t;

// Uso simplificado
operacion_matematica_t operacion = OPERACION_SUMA;

// En lugar de:
// enum operacion_matematica operacion = OPERACION_SUMA;

Definición de tipo enum con typedef

Enumeraciones como Parámetros de Función

Las enumeraciones proporcionan una interfaz más segura y expresiva para funciones que requieren parámetros de un conjunto limitado de valores:

1
2
3
4
5
6
7
8
9
10
typedef enum {
    NIVEL_DEBUG,
    NIVEL_INFO,
    NIVEL_WARNING,
    NIVEL_ERROR,
    NIVEL_CRITICAL
} nivel_log_t;

void escribir_log(nivel_log_t nivel, const char *mensaje) {
    ...

Uso de enums como parámetros de función

Validación y Manejo Seguro

Validación de Rangos

Aunque C no proporciona verificación automática de tipos enum, podés implementar validación explícita:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef enum {
    ESTADO_INICIAL,
    ESTADO_PROCESANDO,
    ESTADO_COMPLETADO,
    ESTADO_ERROR,
    // Centinela para validación
    ESTADO_MAX
} estado_proceso_t;

bool es_estado_valido(int valor) {
    return (valor >= ESTADO_INICIAL && valor < ESTADO_MAX);
}

bool cambiar_estado(estado_proceso_t *estado_actual, estado_proceso_t nuevo_estado) {
    if (!es_estado_valido(nuevo_estado)) {
        fprintf(stderr, "Error: Estado inválido %d\n", nuevo_estado);
        return false;
    }

    *estado_actual = nuevo_estado;
    return true;
}

Validación de valores de enumeración

Patrón de Validación con switch

Una técnica robusta es usar switch con manejo explícito de casos inválidos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef enum {
    FORMATO_JSON,
    FORMATO_XML,
    FORMATO_CSV,
    FORMATO_YAML
} formato_archivo_t;

const char *obtener_extension(formato_archivo_t formato) {
    switch (formato) {
        case FORMATO_JSON:
            return ".json";
        case FORMATO_XML:
            return ".xml";
        case FORMATO_CSV:
            return ".csv";
        case FORMATO_YAML:
            return ".yaml";
        default:
            fprintf(stderr, "Error: formato de archivo desconocido: %d\n", formato);
            return NULL;
    }
}

Validación robusta con switch

Enumeraciones con Flags (Bit Flags)

Para representar combinaciones de opciones, podés usar enumeraciones con valores que son potencias de 2:

1
2
3
4
5
6
typedef enum {
    PERMISO_NINGUNO = 0,
    PERMISO_LECTURA = 1,        // 0001
    PERMISO_ESCRITURA = 2,      // 0010
    PERMISO_EJECUCION = 4,      // 0100
} bit_flag_t;

Enumeración de flags para permisos

Problemas Comunes

1. Falta de Verificación de Tipo

C permite asignar cualquier valor entero a una variable enum sin advertencias:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef enum {
    ROJO,
    VERDE,
    AZUL
} color_t;

color_t color = 42;  // ¡Compilará sin error!

// Esto puede causar problemas en switch statements
switch (color) {
    case ROJO:
        printf("Rojo\n");
        break;
    case VERDE:
        printf("Verde\n");
        break;
    case AZUL:
        printf("Azul\n");
        break;
    // Sin default, el valor 42 no es manejado
}

Problema: asignación de valores arbitrarios

Solución: Siempre incluí un caso default y validación explícita:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
switch (color) {
    case ROJO:
        printf("Rojo\n");
        break;
    case VERDE:
        printf("Verde\n");
        break;
    case AZUL:
        printf("Azul\n");
        break;
    default:
        fprintf(stderr, "Error: color inválido %d\n", color);
        return ERROR_VALOR_INVALIDO;
}

Solución: manejo defensivo

2. Dependencia de Valores Numéricos Específicos

typedef enum {
    ENERO = 1,
    FEBRERO,    // 2
    MARZO       // 3
    // ...
} mes_t;

// Código frágil que depende de valores específicos
int dias_mes[] = {0, 31, 28, 31 /* ... */}; // ¡Índice 0 sin usar!
int dias = dias_mes[mes]; // Asume que ENERO == 1

Problema: dependencia frágil de valores

Solución: Desacoplar la lógica del valor numérico:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef enum {
    ENERO,
    FEBRERO,
    MARZO,
    // ... otros meses
    MES_INVALIDO
} mes_t;

int obtener_dias_mes(mes_t mes) {
    static const int dias_por_mes[] = {
        31,  // ENERO
        28,  // FEBRERO
        31,  // MARZO
        // ... otros meses
    };

    if (mes < 0 || mes >= MES_INVALIDO) {
        return -1;  // Error
    }

    return dias_por_mes[mes];
}

Solución: mapeo explícito

Mejores Prácticas

1. Usar Enumeraciones para Máquinas de Estado

Las enumeraciones son ideales para implementar máquinas de estado finitas:

Máquina de estado con enumeraciones

Figure 2:Ejemplo de una máquina de estados de conexión implementada con enumeraciones, mostrando estados y transiciones válidas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef enum {
    ESTADO_IDLE,
    ESTADO_CONECTANDO,
    ESTADO_AUTENTICANDO,
    ESTADO_CONECTADO,
    ESTADO_DESCONECTANDO,
    ESTADO_ERROR
} estado_conexion_t;

typedef struct {
    estado_conexion_t estado_actual;
    int intentos_reconexion;
    time_t timestamp_ultimo_cambio;
} maquina_estados_t;

bool transicion_valida(estado_conexion_t desde, estado_conexion_t hacia) {
    switch (desde) {
        case ESTADO_IDLE:
            return (hacia == ESTADO_CONECTANDO);
        case ESTADO_CONECTANDO:
            return (hacia == ESTADO_AUTENTICANDO || hacia == ESTADO_ERROR);
        case ESTADO_AUTENTICANDO:
            return (hacia == ESTADO_CONECTADO || hacia == ESTADO_ERROR);
        case ESTADO_CONECTADO:
            return (hacia == ESTADO_DESCONECTANDO || hacia == ESTADO_ERROR);
        case ESTADO_DESCONECTANDO:
            return (hacia == ESTADO_IDLE || hacia == ESTADO_ERROR);
        case ESTADO_ERROR:
            return (hacia == ESTADO_IDLE || hacia == ESTADO_CONECTANDO);
        default:
            return false;
    }
}

Máquina de estado con enumeraciones

2. Definir Funciones de Utilidad

Creá funciones auxiliares para trabajar con enumeraciones de forma segura:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
typedef enum {
    PRIORIDAD_ERRONEA = -1,
    PRIORIDAD_BAJA,
    PRIORIDAD_NORMAL,
    PRIORIDAD_ALTA,
    PRIORIDAD_CRITICA,
    PRIORIDAD_MAX  // Centinela
} prioridad_t;

const char *prioridad_a_string(prioridad_t prioridad) {
    static const char *nombres[] = {
        "Baja",
        "Normal",
        "Alta",
        "Crítica"
    };

    if (prioridad < 0 || prioridad >= PRIORIDAD_MAX) {
        return "Desconocida";
    }

    return nombres[prioridad];
}

prioridad_t string_a_prioridad(const char *str) {
    if (str == NULL) {
        return PRIORIDAD_ERRONEA;
    }

    if (strcmp(str, "Baja") == 0) {
        return PRIORIDAD_BAJA;
    }
    if (strcmp(str, "Normal") == 0) {
        return PRIORIDAD_NORMAL;
    }
    if (strcmp(str, "Alta") == 0) {
        return PRIORIDAD_ALTA;
    }
    if (strcmp(str, "Crítica") == 0) {
        return PRIORIDAD_CRITICA;
    }

    return PRIORIDAD_ERRONEA;  // No encontrado
}

bool es_prioridad_valida(prioridad_t prioridad) {
    return (prioridad >= PRIORIDAD_BAJA && prioridad < PRIORIDAD_MAX);
}

Funciones de utilidad para enums

3. Documentar el Propósito y Rangos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * Estados posibles de una transacción bancaria.
 *
 * TRANSACCION_PENDIENTE: Transacción creada pero no procesada
 * TRANSACCION_PROCESANDO: En curso, no se puede cancelar
 * TRANSACCION_COMPLETADA: Exitosamente finalizada
 * TRANSACCION_FALLIDA: Error durante el procesamiento
 * TRANSACCION_CANCELADA: Cancelada por el usuario o sistema
 *
 * Nota: Los valores numéricos son secuenciales desde 0.
 * No dependas de valores específicos para lógica de negocio.
 */
typedef enum {
    TRANSACCION_PENDIENTE,
    TRANSACCION_PROCESANDO,
    TRANSACCION_COMPLETADA,
    TRANSACCION_FALLIDA,
    TRANSACCION_CANCELADA,
    TRANSACCION_MAX_ESTADOS
} estado_transaccion_t;

Documentación completa de enumeraciones

4. Usar Enumeraciones para Códigos de Retorno

Las enumeraciones proporcionan códigos de retorno más expresivos que simples enteros:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
typedef enum {
    RESULTADO_EXITO = 0,
    RESULTADO_ERROR_PARAMETRO_NULO,
    RESULTADO_ERROR_MEMORIA_INSUFICIENTE,
    RESULTADO_ERROR_ARCHIVO_NO_EXISTE,
    RESULTADO_ERROR_PERMISOS_INSUFICIENTES,
    RESULTADO_ERROR_FORMATO_INVALIDO,
    RESULTADO_ERROR_CONEXION_PERDIDA,
    RESULTADO_ERROR_TIMEOUT,
    RESULTADO_ERROR_DESCONOCIDO
} resultado_operacion_t;

resultado_operacion_t procesar_archivo(const char *ruta_archivo, void **datos_salida) {
    if (ruta_archivo == NULL || datos_salida == NULL) {
        return RESULTADO_ERROR_PARAMETRO_NULO;
    }

    FILE *archivo = fopen(ruta_archivo, "r");
    if (archivo == NULL) {
        return RESULTADO_ERROR_ARCHIVO_NO_EXISTE;
    }

    // ... lógica de procesamiento

    fclose(archivo);
    return RESULTADO_EXITO;
}

// Uso expresivo
resultado_operacion_t resultado = procesar_archivo("datos.txt", &buffer);
if (resultado != RESULTADO_EXITO) {
    fprintf(stderr, "Error procesando archivo: %d\n", resultado);
    return resultado;
}

Códigos de retorno con enumeraciones

Consideraciones de Rendimiento

Tamaño en Memoria

Las enumeraciones tienen el tamaño de un int por defecto, pero el estándar permite al compilador elegir el tipo entero más pequeño que pueda representar todos los valores:

typedef enum {
    PEQUENO_A,
    PEQUENO_B,
    PEQUENO_C
} enum_pequeno_t;

typedef enum {
    GRANDE_A = 0,
    GRANDE_B = 1000000
} enum_grande_t;

// En muchos compiladores:
// sizeof(enum_pequeno_t) podría ser 1 byte
// sizeof(enum_grande_t) será 4 bytes (int)

Análisis de tamaño de enums

Interoperabilidad con Interfaces de Programación (API) del Sistema

Muchas APIs o Interfaces de Programación de Aplicaciones (ver término en el Glosario) del sistema operativo usan enumeraciones. Es importante entender sus valores:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <sys/types.h>
#include <sys/stat.h>

typedef enum {
    TIPO_ARCHIVO_REGULAR,
    TIPO_ARCHIVO_DIRECTORIO,
    TIPO_ARCHIVO_ENLACE,
    TIPO_ARCHIVO_DESCONOCIDO
} tipo_archivo_t;

tipo_archivo_t obtener_tipo_archivo(const char *ruta) {
    struct stat info;
    if (stat(ruta, &info) != 0) {
        return TIPO_ARCHIVO_DESCONOCIDO;
    }

    if (S_ISREG(info.st_mode)) {
        return TIPO_ARCHIVO_REGULAR;
    } else if (S_ISDIR(info.st_mode)) {
        return TIPO_ARCHIVO_DIRECTORIO;
    } else if (S_ISLNK(info.st_mode)) {
        return TIPO_ARCHIVO_ENLACE;
    }

    return TIPO_ARCHIVO_DESCONOCIDO;
}

Integración con APIs del sistema


Documentación de Enumeraciones

La documentación adecuada de enumeraciones es esencial para comunicar el propósito de cada valor, las relaciones entre valores y las restricciones de uso. Al igual que con las estructuras, existen dos enfoques principales para documentar enumeraciones.

Enfoque 1: Bloque de Documentación Único

Este enfoque utiliza un único bloque de comentario antes de la definición de la enumeración para describir su propósito y todos sus valores. Es ideal para enumeraciones simples donde los valores son autoexplicativos.

/**
 * Representa los niveles de severidad de un mensaje de log.
 * 
 * Los niveles están ordenados de menor a mayor severidad.
 * Usá DEBUG para mensajes detallados durante el desarrollo,
 * INFO para eventos normales, WARNING para situaciones anómalas
 * pero recuperables, ERROR para fallos que impiden operaciones
 * específicas, y CRITICAL para fallos que comprometen el sistema.
 * 
 * Valores:
 *   - NIVEL_DEBUG: Información de depuración detallada
 *   - NIVEL_INFO: Mensajes informativos de operación normal
 *   - NIVEL_WARNING: Advertencias que no impiden la operación
 *   - NIVEL_ERROR: Errores que impiden operaciones específicas
 *   - NIVEL_CRITICAL: Fallos críticos del sistema
 */
typedef enum {
    NIVEL_DEBUG,
    NIVEL_INFO,
    NIVEL_WARNING,
    NIVEL_ERROR,
    NIVEL_CRITICAL
} nivel_log_t;

Ventajas:

Desventajas:

Enfoque 2: Documentación Distribuida

Este enfoque combina un bloque de comentario general con comentarios individuales para cada valor. Es preferible cuando cada valor requiere explicación específica o tiene restricciones particulares.

/**
 * Representa los estados posibles de un proceso de compilación.
 * 
 * El proceso debe seguir el flujo: INICIAL -> ANALIZANDO -> COMPILANDO
 * -> ENLAZANDO -> COMPLETADO. En caso de error en cualquier etapa,
 * transiciona a ERROR_* correspondiente.
 */
typedef enum {
    COMPILACION_INICIAL,         // Estado inicial antes de comenzar
    COMPILACION_ANALIZANDO,      // Análisis léxico y sintáctico en progreso
    COMPILACION_COMPILANDO,      // Generación de código objeto
    COMPILACION_ENLAZANDO,       // Enlazado de módulos y bibliotecas
    COMPILACION_COMPLETADO,      // Proceso finalizado exitosamente
    COMPILACION_ERROR_SINTAXIS,  // Error de sintaxis detectado
    COMPILACION_ERROR_SEMANTICO, // Error semántico detectado
    COMPILACION_ERROR_ENLAZADO,  // Error durante el enlazado
    COMPILACION_ERROR_IO         // Error de entrada/salida
} estado_compilacion_t;

Ventajas:

Desventajas:

Ejemplo Completo: Enumeración con Valores Explícitos

Para enumeraciones con valores explícitos o que representan códigos de protocolo, la documentación debe ser exhaustiva:

/**
 * Códigos de estado HTTP más comunes.
 * 
 * Esta enumeración define los códigos de estado definidos en RFC 7231
 * y RFC 7235. Los valores están organizados por categoría:
 *   - 2xx: Respuestas exitosas
 *   - 4xx: Errores del cliente
 *   - 5xx: Errores del servidor
 * 
 * Los valores numéricos son los códigos HTTP estándar y NO deben
 * modificarse para mantener compatibilidad con el protocolo.
 */
typedef enum {
    HTTP_OK = 200,                      // Solicitud exitosa
    HTTP_CREATED = 201,                 // Recurso creado exitosamente
    HTTP_NO_CONTENT = 204,              // Exitosa, sin contenido en respuesta
    
    HTTP_BAD_REQUEST = 400,             // Sintaxis de solicitud inválida
    HTTP_UNAUTHORIZED = 401,            // Autenticación requerida o fallida
    HTTP_FORBIDDEN = 403,               // Servidor rechaza la solicitud
    HTTP_NOT_FOUND = 404,               // Recurso no encontrado
    HTTP_METHOD_NOT_ALLOWED = 405,      // Método HTTP no permitido
    
    HTTP_INTERNAL_SERVER_ERROR = 500,   // Error interno del servidor
    HTTP_NOT_IMPLEMENTED = 501,         // Funcionalidad no implementada
    HTTP_SERVICE_UNAVAILABLE = 503      // Servicio temporalmente no disponible
} codigo_http_t;

Documentación de Enumeraciones con Flags

Para enumeraciones que representan flags combinables, la documentación debe explicar cómo combinarlos:

/**
 * Flags para control de permisos de archivo.
 * 
 * Estos flags pueden combinarse usando el operador OR (|) para
 * especificar múltiples permisos simultáneamente.
 * 
 * Ejemplo de uso:
 *   permisos_t permisos = PERMISO_LECTURA | PERMISO_ESCRITURA;
 * 
 * Para verificar permisos, usá el operador AND (&):
 *   if (permisos & PERMISO_LECTURA) { ... }
 * 
 * IMPORTANTE: Los valores son potencias de 2 para permitir
 * operaciones bitwise. NO modifiques estos valores.
 */
typedef enum {
    PERMISO_NINGUNO = 0,        // Sin permisos (0b0000)
    PERMISO_LECTURA = 1,        // Permite lectura (0b0001)
    PERMISO_ESCRITURA = 2,      // Permite escritura (0b0010)
    PERMISO_EJECUCION = 4,      // Permite ejecución (0b0100)
    PERMISO_ELIMINACION = 8,    // Permite eliminación (0b1000)
    PERMISO_TODOS = 15          // Todos los permisos (0b1111)
} permisos_archivo_t;

Recomendaciones Generales para Enumeraciones

  1. Significado de los valores: Explicá claramente qué representa cada valor y cuándo debe usarse.

  2. Orden y secuencia: Si el orden de los valores es significativo (ej. severidad, estados), documentá esta relación.

  3. Valores explícitos: Si asignás valores explícitos, documentá por qué (compatibilidad con protocolo, serialización, etc.).

  4. Valores centinela: Si incluís valores como _MAX, _INVALID o _UNKNOWN, explicá su propósito.

  5. Restricciones: Documentá cualquier restricción en el uso, transiciones válidas entre estados, o combinaciones permitidas.

  6. Compatibilidad: Si la enumeración se serializa o se usa en interfaces externas, advertí sobre la necesidad de mantener estabilidad de valores.

Para más detalles sobre el estilo de comentarios, consultá la regla 0x0032h sobre cómo escribir comentarios que expliquen el “porqué” y no el “qué”.

Glosario

Espacio de Nombres (Namespace)
Región lógica de un programa diseñada para agrupar identificadores (variables, funciones, tipos) y evitar colisiones de nombres. C no posee namespaces explícitos, sino que organiza sus identificadores en cuatro categorías implícitas de espacios de nombres dentro de cada ámbito.
API (Interfaz de Programación de Aplicaciones)
Conjunto de firmas de funciones, definiciones de tipos y constantes expuestas por una biblioteca o el sistema operativo para permitir que un programa de usuario invoque y consuma sus servicios de forma abstracta.