C语言联合体深度解析:内存复用与协议解析实战
如果你在C语言项目中遇到过这样的场景需要用一个变量存储不同类型的数据但又不想浪费内存空间或者需要处理网络协议包、硬件寄存器等需要按不同方式解释同一块内存的数据结构——那么联合体union就是你必须要掌握的关键技术。很多C语言初学者对联合体的理解停留在“多个成员共用内存”的表面概念但实际上联合体真正的价值在于它提供了一种类型安全的内存复用机制。与结构体为每个成员分配独立空间不同联合体的所有成员共享同一块内存区域这意味着同一时刻只能有一个成员有效。这种特性让联合体在嵌入式系统、协议解析、内存优化等场景中成为不可替代的工具。本文将深入解析C语言联合体的核心原理、使用场景和实际应用。不同于简单的语法介绍我会重点讲解联合体与结构体的本质区别及其内存布局联合体在协议解析中的实际应用案例通过联合体实现数据转换的技巧联合体使用中的常见陷阱与最佳实践读完本文你将不仅理解联合体的语法更能掌握在实际项目中正确、安全使用联合体的能力。1. 联合体要解决的核心问题是什么在深入语法细节之前我们先要明白为什么C语言需要联合体它解决了哪些结构体无法解决的问题1.1 内存效率问题假设你正在开发一个嵌入式设备的数据采集系统需要存储不同类型的传感器数据// 使用结构体的方式 struct SensorData { int type; // 传感器类型 float temperature; // 温度值 int pressure; // 压力值 char status[20]; // 状态信息 };在这个结构体中无论实际存储的是什么类型的数据每个SensorData实例都会占用sizeof(int) sizeof(float) sizeof(int) 20字节的内存。如果系统中有成千上万个这样的数据内存浪费是惊人的。而联合体的核心价值就在于同一时刻只需要存储一种类型的数据为什么要把所有可能类型的空间都分配出来1.2 数据解释的灵活性另一个典型场景是协议解析。比如一个网络数据包前4个字节可能被解释为一个32位整数两个16位整数四个8位字符一个32位浮点数如果没有联合体你需要通过指针转换和位操作来实现这种多解释方式代码会变得复杂且容易出错。1.3 联合体的本质联合体本质上是一种类型安全的共用内存机制。它允许你在同一块内存区域中存储不同类型的数据但编译器会帮你进行类型检查。这与直接使用void指针进行强制类型转换有本质区别类型安全编译器知道所有可能的类型内存对齐编译器会处理对齐问题可读性代码意图更清晰2. 联合体的基础语法与内存布局2.1 联合体的定义联合体的定义语法与结构体相似union Data { int i; float f; char str[20]; };这个定义创建了一个新的类型union Data它包含三个成员i、f、str。但关键点是这三个成员共享同一块内存空间。2.2 内存大小计算联合体的大小由其最大成员决定并且要考虑内存对齐#include stdio.h union Example { int a; // 通常4字节 double b; // 通常8字节 char c[10]; // 10字节 }; int main() { printf(Size of union Example: %lu bytes\n, sizeof(union Example)); // 输出可能是16字节考虑对齐 return 0; }运行这个程序你会发现联合体的大小不是简单的10字节而是16字节在大多数64位系统上。这是因为编译器会按照最大对齐要求来分配空间。2.3 联合体与结构体的内存对比让我们通过一个具体的例子来理解两者的区别#include stdio.h // 结构体每个成员有独立空间 struct StructData { int type; float value; char name[10]; }; // 联合体所有成员共享空间 union UnionData { int int_value; float float_value; char string_value[10]; }; int main() { printf(Size of struct StructData: %lu bytes\n, sizeof(struct StructData)); printf(Size of union UnionData: %lu bytes\n, sizeof(union UnionData)); // 典型输出 // Size of struct StructData: 20 bytes // Size of union UnionData: 12 bytes // 具体值取决于系统和编译器对齐规则 return 0; }从内存角度看结构体type、value、name各有自己的内存区域联合体int_value、float_value、string_value共享同一块内存3. 联合体的基本使用与初始化3.1 声明和初始化联合体变量#include stdio.h union Data { int i; float f; char str[20]; }; int main() { // 方式1声明后分别赋值 union Data data1; data1.i 10; printf(data1.i %d\n, data1.i); // 注意给一个成员赋值后其他成员的值是未定义的 printf(data1.f %f (未定义值)\n, data1.f); // 方式2声明时初始化第一个成员 union Data data2 {25}; printf(data2.i %d\n, data2.i); // 方式3使用指定初始化器C99及以上 union Data data3 {.f 3.14}; printf(data3.f %.2f\n, data3.f); // 方式4使用另一个联合体初始化 union Data data4 data3; printf(data4.f %.2f\n, data4.f); return 0; }3.2 访问联合体成员访问联合体成员使用点运算符.与结构体相同union Data data; data.i 100; // 存储整数 printf(%d\n, data.i); data.f 3.14; // 现在存储浮点数之前的整数值被覆盖 printf(%f\n, data.f); // 注意此时data.i的值是未定义的因为内存已被重新解释3.3 联合体指针联合体指针的使用也与结构体类似union Data data; union Data *ptr data; ptr-i 100; // 通过指针访问成员 printf(%d\n, ptr-i); (*ptr).f 2.718; // 另一种访问方式 printf(%f\n, ptr-f);4. 联合体的典型应用场景4.1 场景一协议数据解析这是联合体最经典的应用场景。假设你正在处理一个网络协议其中数据包的前4字节可以按不同方式解释#include stdio.h #include stdint.h // 用于固定宽度整数类型 // 定义协议数据单元 union ProtocolData { uint32_t raw_data; // 原始32位数据 struct { uint16_t header; // 高16位协议头 uint16_t payload; // 低16位有效载荷 } parts; struct { uint8_t byte0; // 字节0 uint8_t byte1; // 字节1 uint8_t byte2; // 字节2 uint8_t byte3; // 字节3 } bytes; float float_value; // 解释为浮点数 }; void parse_protocol_data(uint32_t network_data) { union ProtocolData pdu; pdu.raw_data network_data; printf(原始数据: 0x%08X\n, pdu.raw_data); printf(作为两个16位整数: 0x%04X, 0x%04X\n, pdu.parts.header, pdu.parts.payload); printf(作为四个字节: 0x%02X, 0x%02X, 0x%02X, 0x%02X\n, pdu.bytes.byte0, pdu.bytes.byte1, pdu.bytes.byte2, pdu.bytes.byte3); printf(作为浮点数: %f\n, pdu.float_value); } int main() { // 测试不同的数据解释 parse_protocol_data(0x40490FDB); // π的浮点数表示 printf(\n); parse_protocol_data(0x12345678); // 测试数据 return 0; }4.2 场景二节省内存的变体记录在资源受限的嵌入式系统中联合体可以显著节省内存#include stdio.h #include string.h // 传感器类型枚举 typedef enum { SENSOR_TEMPERATURE, SENSOR_PRESSURE, SENSOR_HUMIDITY, SENSOR_STATUS } SensorType; // 使用联合体的传感器数据结构 typedef struct { SensorType type; union { float temperature; // 温度值 int pressure; // 压力值 double humidity; // 湿度值 char status[16]; // 状态信息 } value; } SensorData; void print_sensor_data(const SensorData *data) { printf(传感器类型: ); switch(data-type) { case SENSOR_TEMPERATURE: printf(温度 - 值: %.2f°C\n,>#include stdio.h #include stdlib.h #include string.h // 支持的数据类型 typedef enum { VAR_INT, VAR_FLOAT, VAR_STRING, VAR_BOOL } VarType; // 变体类型定义 typedef struct { VarType type; union { int int_val; float float_val; char* string_val; int bool_val; // 0false, 1true } data; } Variant; // 创建变体 Variant create_int(int value) { Variant v; v.type VAR_INT; v.data.int_val value; return v; } Variant create_float(float value) { Variant v; v.type VAR_FLOAT; v.data.float_val value; return v; } Variant create_string(const char* value) { Variant v; v.type VAR_STRING; v.data.string_val malloc(strlen(value) 1); strcpy(v.data.string_val, value); return v; } // 打印变体 void print_variant(const Variant* v) { switch(v-type) { case VAR_INT: printf(整数: %d\n, v-data.int_val); break; case VAR_FLOAT: printf(浮点数: %.2f\n, v-data.float_val); break; case VAR_STRING: printf(字符串: %s\n, v-data.string_val); break; case VAR_BOOL: printf(布尔值: %s\n, v-data.bool_val ? true : false); break; } } // 清理资源 void free_variant(Variant* v) { if(v-type VAR_STRING v-data.string_val ! NULL) { free(v-data.string_val); v-data.string_val NULL; } } int main() { Variant vars[4]; vars[0] create_int(42); vars[1] create_float(3.14159); vars[2] create_string(Hello, Union!); vars[3].type VAR_BOOL; vars[3].data.bool_val 1; for(int i 0; i 4; i) { print_variant(vars[i]); } // 清理 for(int i 0; i 4; i) { free_variant(vars[i]); } return 0; }5. 联合体与结构体的组合使用在实际项目中联合体经常与结构体组合使用形成更复杂的数据结构5.1 匿名联合体C11标准C11标准引入了匿名联合体可以简化代码#include stdio.h // 传统方式需要.value. struct DataOld { int type; union { int i; float f; } value; }; // C11匿名联合体直接访问成员 struct DataNew { int type; union { int i; float f; }; // 匿名联合体 }; int main() { struct DataOld old; old.type 1; old.value.i 100; // 需要.value. struct DataNew new; new.type 1; new.i 100; // 直接访问 printf(传统方式: type%d, i%d\n, old.type, old.value.i); printf(匿名联合体: type%d, i%d\n, new.type, new.i); return 0; }5.2 复杂数据结构示例#include stdio.h #include string.h // 消息类型枚举 typedef enum { MSG_TEXT, MSG_NUMBER, MSG_COORDINATE } MessageType; // 坐标结构体 typedef struct { double x; double y; } Coordinate; // 消息结构体包含匿名联合体 typedef struct { MessageType type; char sender[32]; union { char text[256]; // 文本消息 double number; // 数字消息 Coordinate coord; // 坐标消息 }; } Message; void print_message(const Message* msg) { printf(发送者: %s\n, msg-sender); printf(消息类型: ); switch(msg-type) { case MSG_TEXT: printf(文本消息\n); printf(内容: %s\n, msg-text); break; case MSG_NUMBER: printf(数字消息\n); printf(数值: %.2f\n, msg-number); break; case MSG_COORDINATE: printf(坐标消息\n); printf(坐标: (%.2f, %.2f)\n, msg-coord.x, msg-coord.y); break; } printf(---\n); } int main() { Message messages[3]; // 文本消息 messages[0].type MSG_TEXT; strcpy(messages[0].sender, Alice); strcpy(messages[0].text, Hello, World!); // 数字消息 messages[1].type MSG_NUMBER; strcpy(messages[1].sender, Bob); messages[1].number 3.14159; // 坐标消息 messages[2].type MSG_COORDINATE; strcpy(messages[2].sender, Charlie); messages[2].coord.x 12.34; messages[2].coord.y 56.78; // 打印所有消息 for(int i 0; i 3; i) { print_message(messages[i]); } // 计算内存节省 printf(每个Message的大小: %lu bytes\n, sizeof(Message)); printf(如果不使用联合体估计需要: %lu bytes\n, sizeof(MessageType) 32 256 sizeof(double) sizeof(Coordinate)); return 0; }6. 联合体的高级技巧与注意事项6.1 字节序Endianness问题在处理网络协议或二进制文件时字节序是一个重要考虑因素#include stdio.h #include stdint.h // 检测系统字节序 void check_endianness() { union { uint32_t i; uint8_t c[4]; } test {0x12345678}; printf(数值: 0x%08X\n, test.i); printf(字节表示: 0x%02X 0x%02X 0x%02X 0x%02X\n, test.c[0], test.c[1], test.c[2], test.c[3]); if(test.c[0] 0x78) { printf(系统是小端序 (Little Endian)\n); } else if(test.c[0] 0x12) { printf(系统是大端序 (Big Endian)\n); } } // 网络字节序大端序到主机字节序转换 uint32_t ntohl_manual(uint32_t netlong) { union { uint32_t value; uint8_t bytes[4]; } u; u.value netlong; // 如果是小端系统需要转换 #if __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ return ((uint32_t)u.bytes[0] 24) | ((uint32_t)u.bytes[1] 16) | ((uint32_t)u.bytes[2] 8) | (uint32_t)u.bytes[3]; #else return u.value; // 大端系统不需要转换 #endif } int main() { check_endianness(); printf(\n); uint32_t network_value 0x12345678; uint32_t host_value ntohl_manual(network_value); printf(网络字节序: 0x%08X\n, network_value); printf(转换为主机字节序: 0x%08X\n, host_value); return 0; }6.2 类型双关Type Punning的合法性问题类型双关指的是通过一种类型写入联合体然后通过另一种类型读取。这在C语言中是合法的但在C中是未定义行为#include stdio.h #include stdint.h // C语言中合法的类型双关 float int_bits_to_float(uint32_t i) { union { uint32_t i; float f; } u; u.i i; return u.f; } uint32_t float_bits_to_int(float f) { union { uint32_t i; float f; } u; u.f f; return u.i; } int main() { // 将浮点数的位模式解释为整数 float pi 3.14159f; uint32_t bits float_bits_to_int(pi); printf(浮点数: %f\n, pi); printf(位模式: 0x%08X\n, bits); printf(转换回来: %f\n, int_bits_to_float(bits)); // 特殊值测试 float zero 0.0f; float neg_zero -0.0f; printf(\n0.0的位模式: 0x%08X\n, float_bits_to_int(zero)); printf(-0.0的位模式: 0x%08X\n, float_bits_to_int(neg_zero)); return 0; }重要提示在C中这种类型双关是未定义行为。如果需要在C中实现类似功能应该使用memcpy// C中的安全实现 float int_bits_to_float_cpp(uint32_t i) { float f; memcpy(f, i, sizeof(f)); return f; }6.3 联合体中的数组成员当联合体包含数组成员时需要注意数组的内存布局#include stdio.h union ArrayUnion { int numbers[4]; struct { int a, b, c, d; } elements; long long wide[2]; // 假设long long是8字节 }; int main() { union ArrayUnion u; // 通过数组赋值 for(int i 0; i 4; i) { u.numbers[i] (i 1) * 10; } printf(通过数组设置值:\n); for(int i 0; i 4; i) { printf(numbers[%d] %d\n, i, u.numbers[i]); } printf(\n通过结构体访问:\n); printf(elements.a %d\n, u.elements.a); printf(elements.b %d\n, u.elements.b); printf(elements.c %d\n, u.elements.c); printf(elements.d %d\n, u.elements.d); printf(\n作为两个long long整数:\n); printf(wide[0] %lld\n, u.wide[0]); printf(wide[1] %lld\n, u.wide[1]); // 修改结构体成员会影响数组 u.elements.b 999; printf(\n修改elements.b后:\n); printf(numbers[1] %d\n, u.numbers[1]); return 0; }7. 联合体的常见问题与解决方案7.1 问题一忘记当前有效的成员这是使用联合体时最常见的错误#include stdio.h union Data { int i; float f; char str[20]; }; void problematic_example() { union Data data; data.i 100; printf(存储整数: %d\n, data.i); // 错误没有重置联合体直接读取其他成员 printf(错误地读取浮点数: %f\n, data.f); // 未定义行为 data.f 3.14; printf(存储浮点数: %f\n, data.f); // 错误整数值已被覆盖 printf(错误地读取整数: %d\n, data.i); // 无意义的值 } void correct_example() { union Data data; // 方案1使用标签记录当前有效类型 typedef enum { TYPE_INT, TYPE_FLOAT, TYPE_STRING } DataType; DataType current_type; data.i 100; current_type TYPE_INT; // 读取时检查类型 if(current_type TYPE_INT) { printf(整数: %d\n, data.i); } // 方案2每次使用前重新初始化 data.f 3.14; current_type TYPE_FLOAT; if(current_type TYPE_FLOAT) { printf(浮点数: %f\n, data.f); } }7.2 问题二对齐导致的移植性问题#include stdio.h // 这个联合体在不同平台上的大小可能不同 union AlignmentExample { char c; int i; double d; }; int main() { printf(sizeof(char) %lu\n, sizeof(char)); printf(sizeof(int) %lu\n, sizeof(int)); printf(sizeof(double) %lu\n, sizeof(double)); printf(sizeof(union AlignmentExample) %lu\n, sizeof(union AlignmentExample)); // 查看对齐要求 printf(\n对齐要求:\n); printf(_Alignof(char) %lu\n, _Alignof(char)); printf(_Alignof(int) %lu\n, _Alignof(int)); printf(_Alignof(double) %lu\n, _Alignof(double)); return 0; }解决方案使用标准整数类型如int32_t、uint64_t在需要跨平台时显式处理字节序使用编译器的打包指令如#pragma pack但要谨慎7.3 问题三联合体包含指针时的内存管理#include stdio.h #include stdlib.h #include string.h union PointerUnion { int *int_ptr; float *float_ptr; char *str_ptr; }; void pointer_union_example() { union PointerUnion pu; // 分配内存 pu.int_ptr malloc(sizeof(int) * 10); if(pu.int_ptr NULL) { printf(内存分配失败\n); return; } // 使用int指针 for(int i 0; i 10; i) { pu.int_ptr[i] i * 10; } // 危险通过float指针访问同一内存 printf(通过float指针访问危险:\n); for(int i 0; i 10; i) { printf(%f , pu.float_ptr[i]); // 错误的解释 } printf(\n); // 正确只通过分配时使用的指针类型访问 printf(通过int指针访问正确:\n); for(int i 0; i 10; i) { printf(%d , pu.int_ptr[i]); } printf(\n); // 清理 free(pu.int_ptr); // 注意pu.float_ptr和pu.str_ptr现在都是悬空指针 pu.int_ptr NULL; // 最好将所有指针设为NULL pu.float_ptr NULL; pu.str_ptr NULL; }8. 联合体的最佳实践8.1 实践一总是使用标签记录当前有效类型#include stdio.h #include string.h // 最佳实践联合体标签 typedef struct { enum { INT, FLOAT, STRING } type; union { int int_value; float float_value; char string_value[64]; } data; } TaggedUnion; void set_int(TaggedUnion *tu, int value) { tu-type INT; tu-data.int_value value; } void set_float(TaggedUnion *tu, float value) { tu-type FLOAT; tu-data.float_value value; } void set_string(TaggedUnion *tu, const char *value) { tu-type STRING; strncpy(tu-data.string_value, value, sizeof(tu-data.string_value) - 1); tu-data.string_value[sizeof(tu-data.string_value) - 1] \0; } void print_tagged_union(const TaggedUnion *tu) { switch(tu-type) { case INT: printf(整数: %d\n, tu-data.int_value); break; case FLOAT: printf(浮点数: %.2f\n, tu-data.float_value); break; case STRING: printf(字符串: %s\n, tu-data.string_value); break; default: printf(未知类型\n); } }8.2 实践二为联合体提供安全的访问接口#include stdio.h #include stdbool.h // 安全的联合体封装 typedef union { int i; float f; } SafeUnion; typedef struct { bool is_int; SafeUnion value; } SafeContainer; SafeContainer make_int(int value) { SafeContainer sc; sc.is_int true; sc.value.i value; return sc; } SafeContainer make_float(float value) { SafeContainer sc; sc.is_int false; sc.value.f value; return sc; } bool get_int(const SafeContainer *sc, int *out) { if(sc-is_int out ! NULL) { *out sc-value.i; return true; } return false; } bool get_float(const SafeContainer *sc, float *out) { if(!sc-is_int out ! NULL) { *out sc-value.f; return true; } return false; }8.3 实践三在嵌入式系统中的使用规范// embedded_union.h #ifndef EMBEDDED_UNION_H #define EMBEDDED_UNION_H #include stdint.h // 硬件寄存器映射示例 typedef union { uint32_t raw; // 原始寄存器值 struct { uint32_t enable : 1; // 位0使能位 uint32_t mode : 2; // 位1-2模式选择 uint32_t speed : 4; // 位3-6速度设置 uint32_t reserved : 24; // 位7-31保留位 uint32_t : 0; // 强制对齐到32位边界 } bits; struct { uint8_t byte0; // 低字节 uint8_t byte1; uint8_t byte2; uint8_t byte3; // 高字节 } bytes; } HardwareRegister; // 传感器数据包 typedef struct { uint8_t sensor_id; uint8_t packet_type; union { struct { int16_t temperature; // 温度单位0.1°C uint16_t humidity; // 湿度单位0.1% } environmental; struct { uint16_t pressure; // 压力单位Pa uint16_t altitude; // 海拔单位米 } atmospheric; uint8_t raw_data[4]; // 原始数据 } payload; uint8_t checksum; } SensorPacket; // 函数声明 void init_hardware_register(HardwareRegister *reg); uint8_t calculate_checksum(const SensorPacket *packet); bool validate_packet(const SensorPacket *packet); #endif // EMBEDDED_UNION_H// embedded_union.c #include embedded_union.h #include string.h void init_hardware_register(HardwareRegister *reg) { if(reg NULL) return; reg-raw 0; // 清零寄存器 // 设置默认值 reg-bits.enable 1; // 使能设备 reg-bits.mode 2; // 模式2高速模式 reg-bits.speed 0xF; // 最大速度 // 可以通过bytes访问验证 printf(寄存器值: 0x%08X\n, reg-raw); printf(字节表示: 0x%02X 0x%02X 0x%02X 0x%02X\n, reg-bytes.byte0, reg-bytes.byte1, reg-bytes.byte2, reg-bytes.byte3); } uint8_t calculate_checksum(const SensorPacket *packet) { if(packet NULL) return 0; uint8_t sum 0; const uint8_t *data (const uint8_t*)packet; // 计算除checksum字段外的所有字节和 for(size_t i 0; i sizeof(SensorPacket) - 1; i) { sum data[i]; } return ~sum 1; // 二进制补码 } bool validate_packet(const SensorPacket *packet) { if(packet NULL) return false; uint8_t calculated calculate_checksum(packet); return (calculated packet-checksum); }9. 联合体在实际项目中的综合应用9.1 案例简单的虚拟机实现#include stdio.h #include stdint.h #include stdlib.h // 虚拟机操作码 typedef enum { OP_PUSH_INT, OP_PUSH_FLOAT, OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_PRINT, OP_HALT } OpCode; // 虚拟机指令 typedef struct { OpCode opcode; union { int int_operand; float float_operand; } operand; } Instruction; // 虚拟机栈 typedef union { int int_value; float float_value; } StackValue; // 简单的栈虚拟机 typedef struct { StackValue stack[100]; int stack_ptr; Instruction *program; int pc; // 程序计数器 } VirtualMachine; // 执行程序 void execute_program(VirtualMachine *vm, Instruction *program, int length) { vm-program program; vm-pc 0; vm-stack_ptr -1; while(vm-pc length) { Instruction instr vm-program[vm-pc]; switch(instr.opcode) { case OP_PUSH_INT: vm-stack[vm-stack_ptr].int_value instr.operand.int_operand; break; case OP_PUSH_FLOAT: vm-stack[vm-stack_ptr].float_value instr.operand.float_operand; break; case OP_ADD: if(vm-stack_ptr 1) { // 简单示例假设都是整数 int b vm-stack[vm-stack_ptr--].int_value; int a vm-stack[vm-stack_ptr--].int_value; vm-stack[vm-stack_ptr].int_value a b; } break; case OP_PRINT: if(vm-stack_ptr 0) { printf(栈顶值: %d\n, vm-stack[vm-stack_ptr].int_value); } break; case OP_HALT: return; default: printf(未知指令\n); return; } } } int main() { // 创建一个简单的程序计算 (10 20) * 2 Instruction program[] { {OP_PUSH_INT, .operand.int_operand 10}, {OP_PUSH_INT, .operand.int_operand 20}, {OP_ADD, .operand.int_operand 0}, // operand unused {OP_PUSH_INT, .operand.int_operand 2}, {OP_MUL, .operand.int_operand 0}, {OP_PRINT, .operand.int_operand 0}, {OP_HALT, .operand.int_operand 0} }; VirtualMachine vm; execute_program(vm, program, sizeof(program)/sizeof(program[0])); return 0; }9.2 案例配置系统实现#include stdio.h #include string.h #include stdlib.h // 配置值类型 typedef enum { CONFIG_INT, CONFIG_FLOAT, CONFIG_STRING, CONFIG_BOOL } ConfigType; // 配置项 typedef struct { char key[64]; ConfigType type; union { int int_value; float float_value; char *string_value; int bool_value; } value; } ConfigItem; // 配置管理器 typedef struct { ConfigItem *items; int capacity; int count; } ConfigManager; // 初始化配置管理器 ConfigManager* config_create(int initial_capacity) { ConfigManager *cm malloc(sizeof(ConfigManager)); if(cm NULL) return NULL; cm-items malloc(sizeof(ConfigItem) * initial_capacity); if(cm-items NULL) { free(cm); return NULL; } cm-capacity initial_capacity; cm-count 0; return cm; } // 设置配置值 int config_set_int(ConfigManager *cm, const char *key, int value) { if(cm NULL || key NULL) return 0; // 查找是否已存在 for(int i 0; i cm-count; i) { if(strcmp(cm-items[i].key, key) 0) { cm-items[i].type CONFIG_INT; cm-items[i].value.int_value value; return 1; } } // 添加新项 if(cm-count cm-capacity) { // 扩容 int new_capacity cm-capacity * 2; ConfigItem *new_items realloc(cm-items, sizeof(ConfigItem) * new_capacity); if(new_items NULL) return 0; cm-items new_items; cm-capacity new_capacity; } ConfigItem *item cm-items[cm-count]; strncpy(item-key, key, sizeof(item-key) - 1); item-key[sizeof(item-key) - 1] \0; item-type CONFIG_INT; item-value.int_value value; return 1; } // 获取配置值带默认值 int config_get_int(const ConfigManager *cm, const char *key, int default_value) { if(cm NULL || key NULL) return default_value; for(int i 0; i cm-count; i) { if(strcmp(cm-items[i].key, key) 0 cm-items[i].type CONFIG_INT) { return cm-items[i].value.int_value; } } return default_value; } // 打印所有配置 void config_print_all(const ConfigManager *cm) { if(cm NULL) return; printf(配置项 (%d/%d):\n, cm-count, cm-capacity); printf(\n); for(int i 0; i cm-count; i) { printf(%-20s: , cm-items[i].key); switch(cm-items[i].type) { case CONFIG_INT: printf(%d (int)\n, cm-items[i].value.int_value); break; case CONFIG_FLOAT: printf(%.2f (float)\n, cm-items[i].value.float_value); break; case CONFIG_STRING: printf(%s (string)\n, cm-items[i].value.string_value); break; case CONFIG_BOOL: printf(%s (bool)\n, cm-items[i].value.bool_value ? true : false); break; } } } // 清理资源 void config_destroy(ConfigManager *cm) { if(cm NULL) return; // 释放所有字符串内存 for(int i 0; i cm-count; i) { if(cm-items[i].type CONFIG_STRING cm-items[i].value.string_value ! NULL) { free(cm-items[i].value.string_value); } } free(cm-items); free(cm); } int main() { // 创建配置管理器 ConfigManager *config config_create(10); if(config NULL) { printf(创建配置管理器失败\n); return 1; } // 设置各种类型的配置 config_set_int(config, max_connections, 100); config_set_int(config, timeout, 30); // 获取配置值 int max_conn config_get_int(config, max_connections, 50); int timeout config_get_int(config, timeout, 10); int not_found config_get_int(config, not_exist, 999); // 使用默认值 printf(max_connections: %d\n, max_conn); printf(timeout: %d\n, timeout); printf(not_exist: %d (使用默认值)\n, not_found); // 打印所有配置 config_print_all(config); // 清理 config_destroy(config); return 0; }联合体是C语言中一个强大但容易被误解的特性。正确使用联合体可以显著提高内存效率、简化数据结构的复杂性并在协议解析、嵌入式系统等场景中发挥关键作用。然而联合体也带来了类型安全的风险需要开发者始终保持警惕。关键要点总结联合体的核心是内存共享不是类型转换总是使用标签记录当前有效的成员类型注意字节序和对齐问题特别是在跨平台开发中在C中使用联合体进行类型双关是未定义行为结合结构体使用可以创建更安全、更易用的数据结构在实际项目中联合体最适合的场景包括协议数据解析、硬件寄存器映射、内存受限环境的数据存储、实现变体类型等。对于大多数应用层开发如果内存不是主要瓶颈使用结构体配合指针可能是更安全的选择。