1. 共用体(union)的本质与内存布局共用体是C语言中一种特殊的数据结构它允许在同一块内存区域存储不同类型的数据。想象你有一个收纳盒这个盒子可以放一本书、一个水杯或者一台笔记本电脑但同一时间只能放其中一样东西——共用体就是这样的内存收纳盒。定义共用体的基本语法如下union 共用体名 { 类型1 成员1; 类型2 成员2; // ... };这里有个实际例子union Data { int i; float f; char str[20]; };这个共用体占用的内存大小是多少呢我们来做个实验#include stdio.h int main() { printf(Data大小: %zu字节\n, sizeof(union Data)); return 0; }在我的机器上运行结果是20字节因为char str[20]是最长的成员。这就引出了共用体的核心特性所有成员共享同一块内存共用体的大小等于最大成员的大小。内存布局示意图--------------------- | 20字节 | -- str占满全部空间 --------------------- | 4字节 | | -- i只使用前4字节 ------- | | 4字节 | | -- f也只使用前4字节 --------------------2. 共用体的实战应用场景2.1 寄存器位操作在嵌入式开发中我们经常需要对硬件寄存器进行位操作。比如控制一个LED的状态typedef union { struct { unsigned power:1; // 电源位 unsigned mode:2; // 模式位 unsigned :5; // 保留位 } bits; uint8_t byte; } LED_Register; LED_Register led; led.byte 0x00; // 初始化为全0 led.bits.power 1; // 开启电源 led.bits.mode 2; // 设置为模式2这种用法既保持了位操作的直观性又能以字节为单位整体读写寄存器。2.2 协议帧处理网络通信中不同协议帧往往有不同格式。用共用体可以优雅地处理typedef union { struct { uint8_t type; uint16_t length; uint8_t data[256]; } standard; struct { uint8_t cmd; uint32_t timestamp; uint8_t payload[16]; } control; uint8_t raw[264]; // 原始字节流 } ProtocolPacket; ProtocolPacket packet; recv(socket, packet.raw, sizeof(packet.raw), 0); if(packet.standard.type 0xA1) { // 处理标准帧 } else if(packet.control.cmd 0xB2) { // 处理控制帧 }2.3 类型转换技巧共用体可以实现快速类型转换而不需要指针操作union Converter { float f; uint32_t u; }; float pi 3.14159f; union Converter c; c.f pi; printf(浮点数%f的二进制表示: 0x%08X\n, pi, c.u);这在需要查看浮点数二进制表示时特别有用。3. typedef的魔法为类型赋予新生命typedef就像是给类型起外号让复杂类型变得简单易懂。基本语法typedef 原类型 新类型名;3.1 简化结构体声明没有typedef时struct Student { char name[20]; int age; }; struct Student s1; // 每次都要写struct使用typedef后typedef struct { char name[20]; int age; } Student; Student s1; // 清爽多了3.2 定义函数指针类型处理回调函数时特别有用typedef int (*CompareFunc)(const void*, const void*); void sort(int *array, size_t n, CompareFunc cmp) { // 排序实现 } int compareInt(const void *a, const void *b) { return *(int*)a - *(int*)b; } int main() { int nums[] {3,1,4,2}; sort(nums, 4, compareInt); }3.3 平台无关的类型定义确保代码在不同平台都能正确运行typedef int32_t Int32; // 保证总是32位整数 typedef uint64_t UInt64; // 保证总是64位无符号整数4. 共用体与typedef的强强联合4.1 创建通用值类型typedef union { int i; float f; char *s; } Value; typedef struct { char *key; Value value; int type; // 标记当前存储的类型 } DictEntry;这种模式在实现解释器或配置系统时很常见。4.2 网络字节序转换typedef union { uint32_t value; struct { uint8_t b0; uint8_t b1; uint8_t b2; uint8_t b3; } bytes; } NetworkLong; uint32_t ntohl(uint32_t netlong) { NetworkLong n; n.value netlong; return (n.bytes.b0 24) | (n.bytes.b1 16) | (n.bytes.b2 8) | n.bytes.b3; }4.3 模拟面向对象的多态typedef struct { int type; union { struct { int x, y; } point; struct { int x, y, w, h; } rect; struct { int cx, cy, r; } circle; } shape; } GraphicObject; void draw(GraphicObject *obj) { switch(obj-type) { case POINT: // 画点 break; case RECT: // 画矩形 break; case CIRCLE: // 画圆 break; } }5. 常见陷阱与最佳实践5.1 大小端问题共用体常用于检测系统的大小端typedef union { int i; char c[4]; } EndianTest; int isLittleEndian() { EndianTest test {0x01020304}; return test.c[0] 0x04; }5.2 内存对齐问题考虑这个例子typedef union { struct { char a; int b; } s; char data[8]; } AlignTest;在某些平台上sizeof(AlignTest)可能是8而不是5因为int会按4字节对齐。5.3 最佳实践总结明确当前有效成员使用额外变量标记共用体当前存储的类型避免直接拷贝特别是包含指针成员的共用体注意平台差异大小端和对齐方式会影响共用体行为合理使用typedef让代码更清晰但不要过度使用文档化复杂类型特别是共用体和复杂typedef组合时6. 综合案例学生教师信息管理系统让我们用所学知识实现一个综合案例#include stdio.h #include string.h typedef enum { STUDENT, TEACHER } PersonType; typedef struct { char name[20]; int id; char gender; // M或F PersonType type; union { struct { float score; } student; struct { char course[20]; } teacher; } info; } Person; void printPerson(const Person *p) { printf(姓名: %s\nID: %d\n性别: %c\n, p-name, p-id, p-gender); if(p-type STUDENT) { printf(类型: 学生\n分数: %.1f\n, p-info.student.score); } else { printf(类型: 教师\n课程: %s\n, p-info.teacher.course); } } int main() { Person people[2]; // 学生信息 strcpy(people[0].name, 张三); people[0].id 1001; people[0].gender M; people[0].type STUDENT; people[0].info.student.score 89.5f; // 教师信息 strcpy(people[1].name, 李老师); people[1].id 2001; people[1].gender F; people[1].type TEACHER; strcpy(people[1].info.teacher.course, 计算机科学); for(int i 0; i 2; i) { printPerson(people[i]); printf(\n); } return 0; }这个例子展示了如何用共用体高效地处理不同类型的数据同时保持代码的清晰性。typedef的使用让Person类型更易于理解和使用。
C语言 超详细 零基础入门 共用体(union)与typedef的实战应用与内存解析
发布时间:2026/7/15 4:53:25
1. 共用体(union)的本质与内存布局共用体是C语言中一种特殊的数据结构它允许在同一块内存区域存储不同类型的数据。想象你有一个收纳盒这个盒子可以放一本书、一个水杯或者一台笔记本电脑但同一时间只能放其中一样东西——共用体就是这样的内存收纳盒。定义共用体的基本语法如下union 共用体名 { 类型1 成员1; 类型2 成员2; // ... };这里有个实际例子union Data { int i; float f; char str[20]; };这个共用体占用的内存大小是多少呢我们来做个实验#include stdio.h int main() { printf(Data大小: %zu字节\n, sizeof(union Data)); return 0; }在我的机器上运行结果是20字节因为char str[20]是最长的成员。这就引出了共用体的核心特性所有成员共享同一块内存共用体的大小等于最大成员的大小。内存布局示意图--------------------- | 20字节 | -- str占满全部空间 --------------------- | 4字节 | | -- i只使用前4字节 ------- | | 4字节 | | -- f也只使用前4字节 --------------------2. 共用体的实战应用场景2.1 寄存器位操作在嵌入式开发中我们经常需要对硬件寄存器进行位操作。比如控制一个LED的状态typedef union { struct { unsigned power:1; // 电源位 unsigned mode:2; // 模式位 unsigned :5; // 保留位 } bits; uint8_t byte; } LED_Register; LED_Register led; led.byte 0x00; // 初始化为全0 led.bits.power 1; // 开启电源 led.bits.mode 2; // 设置为模式2这种用法既保持了位操作的直观性又能以字节为单位整体读写寄存器。2.2 协议帧处理网络通信中不同协议帧往往有不同格式。用共用体可以优雅地处理typedef union { struct { uint8_t type; uint16_t length; uint8_t data[256]; } standard; struct { uint8_t cmd; uint32_t timestamp; uint8_t payload[16]; } control; uint8_t raw[264]; // 原始字节流 } ProtocolPacket; ProtocolPacket packet; recv(socket, packet.raw, sizeof(packet.raw), 0); if(packet.standard.type 0xA1) { // 处理标准帧 } else if(packet.control.cmd 0xB2) { // 处理控制帧 }2.3 类型转换技巧共用体可以实现快速类型转换而不需要指针操作union Converter { float f; uint32_t u; }; float pi 3.14159f; union Converter c; c.f pi; printf(浮点数%f的二进制表示: 0x%08X\n, pi, c.u);这在需要查看浮点数二进制表示时特别有用。3. typedef的魔法为类型赋予新生命typedef就像是给类型起外号让复杂类型变得简单易懂。基本语法typedef 原类型 新类型名;3.1 简化结构体声明没有typedef时struct Student { char name[20]; int age; }; struct Student s1; // 每次都要写struct使用typedef后typedef struct { char name[20]; int age; } Student; Student s1; // 清爽多了3.2 定义函数指针类型处理回调函数时特别有用typedef int (*CompareFunc)(const void*, const void*); void sort(int *array, size_t n, CompareFunc cmp) { // 排序实现 } int compareInt(const void *a, const void *b) { return *(int*)a - *(int*)b; } int main() { int nums[] {3,1,4,2}; sort(nums, 4, compareInt); }3.3 平台无关的类型定义确保代码在不同平台都能正确运行typedef int32_t Int32; // 保证总是32位整数 typedef uint64_t UInt64; // 保证总是64位无符号整数4. 共用体与typedef的强强联合4.1 创建通用值类型typedef union { int i; float f; char *s; } Value; typedef struct { char *key; Value value; int type; // 标记当前存储的类型 } DictEntry;这种模式在实现解释器或配置系统时很常见。4.2 网络字节序转换typedef union { uint32_t value; struct { uint8_t b0; uint8_t b1; uint8_t b2; uint8_t b3; } bytes; } NetworkLong; uint32_t ntohl(uint32_t netlong) { NetworkLong n; n.value netlong; return (n.bytes.b0 24) | (n.bytes.b1 16) | (n.bytes.b2 8) | n.bytes.b3; }4.3 模拟面向对象的多态typedef struct { int type; union { struct { int x, y; } point; struct { int x, y, w, h; } rect; struct { int cx, cy, r; } circle; } shape; } GraphicObject; void draw(GraphicObject *obj) { switch(obj-type) { case POINT: // 画点 break; case RECT: // 画矩形 break; case CIRCLE: // 画圆 break; } }5. 常见陷阱与最佳实践5.1 大小端问题共用体常用于检测系统的大小端typedef union { int i; char c[4]; } EndianTest; int isLittleEndian() { EndianTest test {0x01020304}; return test.c[0] 0x04; }5.2 内存对齐问题考虑这个例子typedef union { struct { char a; int b; } s; char data[8]; } AlignTest;在某些平台上sizeof(AlignTest)可能是8而不是5因为int会按4字节对齐。5.3 最佳实践总结明确当前有效成员使用额外变量标记共用体当前存储的类型避免直接拷贝特别是包含指针成员的共用体注意平台差异大小端和对齐方式会影响共用体行为合理使用typedef让代码更清晰但不要过度使用文档化复杂类型特别是共用体和复杂typedef组合时6. 综合案例学生教师信息管理系统让我们用所学知识实现一个综合案例#include stdio.h #include string.h typedef enum { STUDENT, TEACHER } PersonType; typedef struct { char name[20]; int id; char gender; // M或F PersonType type; union { struct { float score; } student; struct { char course[20]; } teacher; } info; } Person; void printPerson(const Person *p) { printf(姓名: %s\nID: %d\n性别: %c\n, p-name, p-id, p-gender); if(p-type STUDENT) { printf(类型: 学生\n分数: %.1f\n, p-info.student.score); } else { printf(类型: 教师\n课程: %s\n, p-info.teacher.course); } } int main() { Person people[2]; // 学生信息 strcpy(people[0].name, 张三); people[0].id 1001; people[0].gender M; people[0].type STUDENT; people[0].info.student.score 89.5f; // 教师信息 strcpy(people[1].name, 李老师); people[1].id 2001; people[1].gender F; people[1].type TEACHER; strcpy(people[1].info.teacher.course, 计算机科学); for(int i 0; i 2; i) { printPerson(people[i]); printf(\n); } return 0; }这个例子展示了如何用共用体高效地处理不同类型的数据同时保持代码的清晰性。typedef的使用让Person类型更易于理解和使用。