【小曾的C语言复习笔记01】结构体、联合体、枚举一次讲透,内存对齐原来没那么难 小曾同学 · 25软件工程原创 · C语言打卡笔记 第1篇复盘整理版哈喽我是小曾。这个系列是我C语言阶段学习笔记的复盘整理版把当时零零散散的打卡重新梳理成体系化的文章。第1篇聊聊C语言的三大自定义类型结构体、联合体、枚举。 文章目录1. 结构体 struct把散数据捏成一个整体2. 内存对齐为什么 sizeof 结果总比你想象的大3. 位段把内存抠到比特级4. 联合体 union一块内存多种解读5. 枚举 enum#define 的优雅升级版6. 小结 思考题01结构体 struct把散数据捏成一个整体C语言的内置类型int、char、double……只能描述单一数据但现实中一个学生有姓名、年龄、成绩好几个属性。结构体就是用来把不同类型的数据打包成一个新的复合类型#include stdio.h #include string.h struct Student { char name[20]; int age; double score; }; int main() { struct Student s; strcpy(s.name, 小明); s.age 18; s.score 95.5; printf(%s %d %.1f\n, s.name, s.age, s.score); return 0; }结构体指针访问成员用-这是后面数据结构链表、树的基本功struct Student* p s; p-age 19; // 等价于 (*p).age 19; printf(%d\n, p-age);02内存对齐为什么 sizeof 结果总比你想象的大学结构体绕不开的一个坑sizeof 算出来的大小经常不是成员大小简单相加。看例子struct S1 { char c1; // 1字节 int i; // 4字节 char c2; // 1字节 }; // 直观感觉1 4 1 6 字节 // 实际 sizeof(struct S1) 12 字节2.1 对齐规则三条背下来第一个成员放在偏移量 0 处其他成员放在自己对齐数的整数倍偏移处对齐数 min(成员大小, 默认对齐数8)结构体总大小必须是最大对齐数的整数倍不够就填充套到S1上画个内存图偏移: 0 1-3(浪费) 4-7 8 9-11(浪费) 内容: c1 填充 i c2 尾部填充 1字节 3字节 4字节 1字节 3字节 总计 12 字节12 是最大对齐数 4 的整数倍 ✅2.2 为什么要对齐平台原因某些硬件平台只能在特定地址读取特定类型不对齐直接报错性能原因对齐后 CPU 一次就能取完数据不用拼接两次读取的结果本质是拿空间换时间2.3 自定义对齐数 #pragma pack默认对齐数VS下是8可以改网络编程、嵌入式里经常用#pragma pack(1) // 按1字节对齐等于取消对齐 struct S2 { char c1; int i; char c2; }; // sizeof 6没有填充了 #pragma pack() // 恢复默认省内存技巧同样的成员把小的放一起、大的放一起可以减少填充浪费。char,char,int占 8 字节而char,int,char占 12 字节。03位段把内存抠到比特级如果一个成员只需要表示 0~3用 int 存太浪费4字节32位只用2位。位段bit-field允许我们按比特位来分配成员空间struct Flags { unsigned int a : 2; // a 只占 2 个比特位 unsigned int b : 5; // b 占 5 个比特位 unsigned int c : 10; // c 占 10 个比特位 }; // sizeof 通常 4一个 unsigned int 单元内挤下位段的典型用途网络协议的数据包头部。比如 TCP 报文头里各种标志位用位段可以精确控制每个字段占几位减轻数据传输时的内存负担提升效率。⚠️位段的两个大坑1.跨平台不确定位段成员在内存中从左往右还是从右往左分配标准没有规定不同编译器行为可能不同跨平台代码慎用2.不能用取地址符 位段成员的起始地址可能不是整字节地址这个概念对它不成立所以scanf(%d, flags.a)这种写法是错的04联合体 union一块内存多种解读联合体也叫共用体的所有成员共用同一块内存这块内存的大小按最大成员来开辟。同一时间只能有效使用其中一个成员union U { int i; char c; }; int main() { union U u; u.i 0x11223344; printf(%x\n, u.c); // 打印 44小端机器上 // 改了 ic 看到的是同一块内存的低字节 return 0; }4.1 经典应用判断电脑大小端这是我在学习时觉得最巧妙的用法面试也常考int check_endian() { union { int i; char c; } u; u.i 1; // 小端01 00 00 00低地址存 01u.c 1 // 大端00 00 00 01低地址存 00u.c 0 return u.c; // 返回1是小端返回0是大端 }原理就是联合体成员共享内存i和c看的是同一块空间的同一段字节只是解读方式不同。05枚举 enum#define 的优雅升级版要表示星期一到星期日你可以写 7 个 #define但枚举明显更优雅// ❌ #define 方式 #define MON 1 #define TUE 2 // ... 写7遍松散无关联 // ✅ 枚举方式 enum Weekday { MON 1, // 可指定初值 TUE, // 自动 2 WED, THU, FRI, SAT, SUN // 依次递增到 7 };我当时的理解是枚举就是 #define 的规模化包装运用。但它比 #define 强在哪对比项#defineenum 枚举类型检查无纯文本替换有是类型调试可见性预编译后被替换调试看不见名字调试时能看到枚举名批量定义一行一个自动递增作用域全局生效易污染遵循作用域规则06小结 思考题 本篇总结结构体打包多种类型数据内存对齐三规则要背本质是空间换时间#pragma pack(n)自定义对齐数省内存场景用位段比特级分配省空间但跨平台不确定、不能取地址联合体成员共享一块内存经典应用是判断大小端枚举#define 的升级版有类型、可调试、自动递增 思考题1. 计算 sizeof在 64 位 VS 环境默认对齐数8下struct Test { double d; char c; int i; };的大小是多少2. 把成员顺序改成char c; int i; double d;大小变成多少3. 用联合体写一个函数判断大小端并在 main 里验证你的机器是哪种。建议大家先自己动手算一算、敲一敲参考答案我会置顶到评论区记得来对答案 下一篇预告结构体搞懂了下一篇聊C语言最刺激的部分——动态内存管理malloc/calloc/realloc/free 四兄弟还有让无数新手栽跟头的野指针问题以及结构体里藏着的柔性数组黑科技。C语言 结构体 内存对齐 联合体 枚举 学习笔记✍️ 本文由「小曾同学」原创25软件工程 · C语言打卡笔记复盘版 系列小曾的C语言打卡笔记 —— 把零散笔记沉淀成体系觉得有用就点个赞 关注小曾下一篇动态内存管理见