前置知识: C

枚举与typedef

3 minBeginner2026/6/14

枚举类型与类型别名

概述

枚举(enum)和类型别名(typedef)是C语言中两种重要的型定义工具。枚举用于定义一组命名的整数常量,使代码更具可读性;typedef 用于为已有型创建新的名称,简化复杂型声明并提高可移植性。两者结合使用可以显著提升代码的清晰度和维护性。

基础概念

枚举的本质

枚举型在C语言中本质上是整数型。每个枚举常量都是一个 int 型的值,编译器将枚举变量视为 int(或兼容的整数型)来处理。

typedef 的作用

typedef 不创建新型,而是为已有型创建一个别名。它在以下场景中特别有用:

  • 简化复杂的型声明(如函数指针
  • 提高代码可移植性(如 uint32_t 在不同平台上可能映射到不同的基础型)
  • 增强代码可读性

快速上手

定义和使用枚举

#include <stdio.h>

// 定义枚举类型
enum Color { RED, GREEN, BLUE };

int main(void) {
    // 声明枚举变量
    enum Color favorite = GREEN;

    // 枚举值就是整数
    printf("RED = %d\n", RED);     // 输出: 0
    printf("GREEN = %d\n", GREEN); // 输出: 1
    printf("BLUE = %d\n", BLUE);   // 输出: 2

    // 可以在 switch 中使用
    switch (favorite) {
        case RED:   printf("红色\n"); break;
        case GREEN: printf("绿色\n"); break;
        case BLUE:  printf("蓝色\n"); break;
    }

    return 0;
}

使用 typedef 创建别名

#include <stdio.h>

// 为基本类型创建别名
typedef unsigned long ulong;
typedef unsigned char byte;

// 为结构体创建别名
typedef struct {
    double x;
    double y;
} Point;

int main(void) {
    ulong big_num = 123456789UL;
    byte data[4] = {0x01, 0x02, 0x03, 0x04};

    Point p = {1.0, 2.0};
    printf("点: (%.1f, %.1f)\n", p.x, p.y);
    printf("大数: %lu\n", big_num);

    return 0;
}

详细用法

枚举的值指定

// 默认从0开始递增
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN };
// MON=0, TUE=1, ..., SUN=6

// 手动指定值
enum HttpStatus {
    OK = 200,
    CREATED = 201,
    BAD_REQUEST = 400,
    NOT_FOUND = 404,
    INTERNAL_ERROR = 500
};

// 部分指定:未指定的值自动递增
enum Priority {
    LOW = 1,
    MEDIUM,    // 自动为2
    HIGH,      // 自动为3
    URGENT = 10,
    CRITICAL   // 自动为11
};

// 可以有重复的值
enum Direction {
    UP = 1,
    DOWN = -1,
    LEFT = -2,
    RIGHT = 2
};

枚举与 typedef 结合

#include <stdio.h>

// 使用 typedef 简化枚举类型名
typedef enum {
    STATE_IDLE,
    STATE_RUNNING,
    STATE_PAUSED,
    STATE_STOPPED
} State;

// 使用时不需要 enum 前缀
State current_state = STATE_IDLE;

const char *state_to_string(State s) {
    switch (s) {
        case STATE_IDLE:    return "空闲";
        case STATE_RUNNING: return "运行中";
        case STATE_PAUSED:  return "已暂停";
        case STATE_STOPPED: return "已停止";
        default:            return "未知";
    }
}

int main(void) {
    current_state = STATE_RUNNING;
    printf("当前状态: %s\n", state_to_string(current_state));
    return 0;
}

typedef 与函数指针

#include <stdio.h>
#include <stdlib.h>

// 不使用 typedef:函数指针声明很复杂
// int (*comparator)(const void *, const void *);

// 使用 typedef:简洁明了
typedef int (*Comparator)(const void *, const void *);

// 升序比较函数
int ascending(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

// 降序比较函数
int descending(const void *a, const void *b) {
    return *(int *)b - *(int *)a;
}

// 使用函数指针作为参数
void sort_array(int *arr, int n, Comparator cmp) {
    qsort(arr, n, sizeof(int), cmp);
}

int main(void) {
    int arr[] = {5, 2, 8, 1, 9, 3};
    int n = sizeof(arr) / sizeof(arr[0]);

    // 升序排序
    sort_array(arr, n, ascending);
    printf("升序: ");
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    printf("\n");

    // 降序排序
    sort_array(arr, n, descending);
    printf("降序: ");
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    printf("\n");

    return 0;
}

typedef 与数组

#include <stdio.h>

// 定义数组类型别名
typedef int IntArray[10];
typedef char Name[32];

int main(void) {
    IntArray scores = {90, 85, 92, 78, 95, 88, 76, 91, 87, 83};
    Name student = "张三";

    printf("学生: %s\n", student);
    for (int i = 0; i < 10; i++) {
        printf("科目%d: %d\n", i + 1, scores[i]);
    }

    return 0;
}

常见场景

场景一:状态机

#include <stdio.h>
#include <stdbool.h>

typedef enum {
    STATE_INIT,
    STATE_CONNECTING,
    STATE_CONNECTED,
    STATE_DISCONNECTING,
    STATE_ERROR
} ConnectionState;

typedef struct {
    ConnectionState state;
    int retry_count;
} Connection;

const char *get_state_name(ConnectionState s) {
    static const char *names[] = {
        "初始化", "连接中", "已连接", "断开中", "错误"
    };
    return names[s];
}

void handle_connection(Connection *conn) {
    switch (conn->state) {
        case STATE_INIT:
            printf("[%s] 准备连接\n", get_state_name(conn->state));
            conn->state = STATE_CONNECTING;
            break;
        case STATE_CONNECTING:
            printf("[%s] 正在建立连接\n", get_state_name(conn->state));
            conn->state = STATE_CONNECTED;
            break;
        case STATE_CONNECTED:
            printf("[%s] 连接正常\n", get_state_name(conn->state));
            conn->state = STATE_DISCONNECTING;
            break;
        case STATE_DISCONNECTING:
            printf("[%s] 正在断开\n", get_state_name(conn->state));
            conn->state = STATE_INIT;
            break;
        case STATE_ERROR:
            printf("[%s] 连接错误\n", get_state_name(conn->state));
            break;
    }
}

int main(void) {
    Connection conn = {STATE_INIT, 0};

    for (int i = 0; i < 5; i++) {
        handle_connection(&conn);
    }

    return 0;
}

场景二:错误码定义

#include <stdio.h>

typedef enum {
    ERR_NONE = 0,
    ERR_INVALID_PARAM = -1,
    ERR_OUT_OF_MEMORY = -2,
    ERR_FILE_NOT_FOUND = -3,
    ERR_PERMISSION_DENIED = -4,
    ERR_TIMEOUT = -5,
    ERR_NETWORK = -6
} ErrorCode;

const char *error_message(ErrorCode err) {
    switch (err) {
        case ERR_NONE:             return "成功";
        case ERR_INVALID_PARAM:    return "参数无效";
        case ERR_OUT_OF_MEMORY:    return "内存不足";
        case ERR_FILE_NOT_FOUND:   return "文件未找到";
        case ERR_PERMISSION_DENIED: return "权限不足";
        case ERR_TIMEOUT:          return "操作超时";
        case ERR_NETWORK:          return "网络错误";
        default:                   return "未知错误";
    }
}

// 模拟一个可能失败的操作
ErrorCode read_config(const char *path) {
    if (!path) return ERR_INVALID_PARAM;
    if (path[0] == '\0') return ERR_INVALID_PARAM;
    // 模拟文件不存在
    return ERR_FILE_NOT_FOUND;
}

int main(void) {
    ErrorCode err = read_config("");
    if (err != ERR_NONE) {
        printf("错误: %s (代码: %d)\n", error_message(err), err);
    }
    return 0;
}

场景三:可移植的型定义

#include <stdio.h>
#include <stdint.h>

// 使用 typedef 定义平台无关的类型
typedef uint8_t  u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;

typedef int8_t  s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;

// 定义回调函数类型
typedef void (*EventCallback)(u32 event_id, void *user_data);

// 定义结果类型
typedef struct {
    s32 code;
    const char *message;
} Result;

// 使用示例
void on_event(u32 event_id, void *user_data) {
    printf("事件 %u 触发, 用户数据: %s\n", event_id, (char *)user_data);
}

int main(void) {
    u8 byte_val = 255;
    u32 counter = 1000000;
    s64 timestamp = 1700000000LL;

    printf("字节: %u\n", byte_val);
    printf("计数器: %u\n", counter);
    printf("时间戳: %lld\n", timestamp);

    EventCallback cb = on_event;
    cb(1, "测试数据");

    Result res = {0, "操作成功"};
    printf("结果: [%d] %s\n", res.code, res.message);

    return 0;
}

注意事项

枚举值的范围

C标准规定枚举型兼容 int,但枚举常量的实际型由实现定义。不要假设枚举值一定是正数或一定在某个范围内:

enum Flags {
    FLAG_A = 1,
    FLAG_B = 2,
    FLAG_C = 4
};

// 枚举值可以按位组合,但类型安全性不如 C++ 的 enum class
int combined = FLAG_A | FLAG_C; // 合法但类型不严格

枚举与整数隐式转换

C语言允许枚举和整数之间的隐式转换,这可能导致意外行为:

enum Color { RED, GREEN, BLUE };
enum Color c = 5; // 合法!5不在枚举范围内

// 更安全的做法:使用函数验证
int is_valid_color(int val) {
    return val >= RED && val <= BLUE;
}

typedef 不是型安全

typedef 创建的是别名而非新型,两个不同的 typedef 可能实际上是同一型:

typedef int Celsius;
typedef int Fahrenheit;

Celsius temp_c = 25;
Fahrenheit temp_f = temp_c; // 编译通过!但语义错误

枚举名的作用域

枚举常量的作用域与普通标识符相同,不同枚举中不能有同名常量:

// 错误:重复定义
enum Color { RED, GREEN, BLUE };
enum Signal { RED, YELLOW, GREEN }; // 编译错误:RED 和 GREEN 重复

// 解决方案:加前缀
enum Color { COLOR_RED, COLOR_GREEN, COLOR_BLUE };
enum Signal { SIGNAL_RED, SIGNAL_YELLOW, SIGNAL_GREEN };

进阶用法

使用枚举实现位标志

#include <stdio.h>

typedef enum {
    PERM_READ    = 1 << 0,  // 1
    PERM_WRITE   = 1 << 1,  // 2
    PERM_EXECUTE = 1 << 2,  // 4
    PERM_DELETE  = 1 << 3   // 8
} Permission;

// 检查权限
int has_permission(int perms, Permission perm) {
    return (perms & perm) != 0;
}

// 添加权限
int add_permission(int perms, Permission perm) {
    return perms | perm;
}

// 移除权限
int remove_permission(int perms, Permission perm) {
    return perms & ~perm;
}

int main(void) {
    // 读写权限
    int user_perms = PERM_READ | PERM_WRITE;

    printf("读权限: %s\n", has_permission(user_perms, PERM_READ) ? "有" : "无");
    printf("执行权限: %s\n", has_permission(user_perms, PERM_EXECUTE) ? "有" : "无");

    // 添加执行权限
    user_perms = add_permission(user_perms, PERM_EXECUTE);
    printf("添加执行后: %s\n", has_permission(user_perms, PERM_EXECUTE) ? "有" : "无");

    // 移除写权限
    user_perms = remove_permission(user_perms, PERM_WRITE);
    printf("移除写后: %s\n", has_permission(user_perms, PERM_WRITE) ? "有" : "无");

    return 0;
}

X-Macro 技巧自动生成枚举和字符串映射

#include <stdio.h>

// 定义枚举项列表(单一定义点)
#define FRUIT_LIST \
    X(APPLE)       \
    X(BANANA)      \
    X(CHERRY)      \
    X(DURIAN)      \
    X(ELDERBERRY)

// 生成枚举定义
typedef enum {
    #define X(name) FRUIT_##name,
    FRUIT_LIST
    #undef X
    FRUIT_COUNT // 自动计算枚举项数量
} Fruit;

// 生成字符串数组
static const char *fruit_names[] = {
    #define X(name) #name,
    FRUIT_LIST
    #undef X
};

const char *fruit_to_string(Fruit f) {
    if (f >= 0 && f < FRUIT_COUNT) {
        return fruit_names[f];
    }
    return "未知";
}

int main(void) {
    for (Fruit f = 0; f < FRUIT_COUNT; f++) {
        printf("FRUIT_%s = %d\n", fruit_to_string(f), f);
    }
    // 输出:
    // FRUIT_APPLE = 0
    // FRUIT_BANANA = 1
    // FRUIT_CHERRY = 2
    // FRUIT_DURIAN = 3
    // FRUIT_ELDERBERRY = 4

    return 0;
}

使用 typedef 简化回调架构

#include <stdio.h>
#include <stdlib.h>

// 定义事件类型
typedef enum {
    EVENT_CLICK,
    EVENT_HOVER,
    EVENT_KEY_PRESS
} EventType;

// 定义事件结构
typedef struct {
    EventType type;
    int x;
    int y;
    int key_code;
} Event;

// 定义回调函数类型
typedef void (*EventHandler)(const Event *event);

// 事件处理器注册表
#define MAX_HANDLERS 10
typedef struct {
    EventHandler handlers[MAX_HANDLERS];
    int count;
} EventSystem;

void event_system_init(EventSystem *es) {
    es->count = 0;
}

void event_system_subscribe(EventSystem *es, EventHandler handler) {
    if (es->count < MAX_HANDLERS) {
        es->handlers[es->count++] = handler;
    }
}

void event_system_emit(EventSystem *es, const Event *event) {
    for (int i = 0; i < es->count; i++) {
        es->handlers[i](event);
    }
}

// 具体的事件处理器
void on_click(const Event *e) {
    printf("点击事件: (%d, %d)\n", e->x, e->y);
}

void on_key(const Event *e) {
    printf("按键事件: 键码 %d\n", e->key_code);
}

void logger(const Event *e) {
    printf("[日志] 事件类型: %d\n", e->type);
}

int main(void) {
    EventSystem es;
    event_system_init(&es);

    event_system_subscribe(&es, on_click);
    event_system_subscribe(&es, on_key);
    event_system_subscribe(&es, logger);

    Event click = {EVENT_CLICK, 100, 200, 0};
    event_system_emit(&es, &click);

    Event key = {EVENT_KEY_PRESS, 0, 0, 65};
    event_system_emit(&es, &key);

    return 0;
}