1. 引言今天学习了 C 语言中的结构体struct和共同体union也叫联合体这是 C 语言中非常重要的复合数据类型。结构体让我们能把不同类型的数据打包成一个整体而共同体则让多个成员共享同一块内存。这篇笔记记录一下今天的学习心得和关键知识点。2. 结构体struct结构体是一种用户自定义的复合数据类型它把若干个不同类型的成员组合在一起形成一个整体。2.1 定义与声明structStudent{charname[20];intage;floatscore;};定义结构体后就可以用它来声明变量structStudentstu1;structStudentstu2{张三,18,89.5};2.2 访问成员通过点运算符.访问结构体变量的成员printf(姓名%s\n,stu1.name);stu1.age19;2.3 结构体数组结构体也可以作为数组的元素structStudentclass[50];class[0].age18;2.4 结构体指针用指针访问结构体成员时使用箭头运算符-structStudent*pstu1;p-age20;// 等价于 (*p).age 20;2.5 结构体大小与内存对齐结构体的大小并不一定等于所有成员大小之和因为存在内存对齐机制。内存对齐是编译器为了提高 CPU 访问内存的效率而采取的一种策略CPU 读取内存时按固定字节数如 4 字节或 8 字节进行如果数据没有对齐可能需要多次读取才能取到完整数据。2.5.1 对齐规则内存对齐遵循以下几条核心规则每个成员都有自己的对齐数成员的对齐数等于其自身大小如char为 1int为 4double为 8但不超过编译器的默认对齐数如 32 位下默认 464 位下默认 8。成员存放的起始地址必须是对齐数的整数倍编译器会在成员之间插入填充字节padding来满足这一要求。结构体总大小必须是对齐数的整数倍结构体末尾也可能有填充字节以保证结构体数组相邻元素也能对齐。结构体的对齐数取所有成员对齐数的最大值。2.5.2 示例分析structExample{chara;// 1 字节对齐数 1intb;// 4 字节对齐数 4};在 32 位系统上sizeof(struct Example)通常是 8 而不是 5a占第 0 字节b需要从 4 的整数倍地址开始因此第 1~3 字节被填充b占第 4~7 字节总大小为 8正好是最大对齐数 4 的整数倍。2.5.3 成员顺序影响大小成员顺序不同结构体大小也可能不同structOrder1{charc;// 1 字节inti;// 4 字节chard;// 1 字节};// sizeof(struct Order1) 12structOrder2{inti;// 4 字节charc;// 1 字节chard;// 1 字节};// sizeof(struct Order2) 8Order1中int需要对齐c后填充 3 字节末尾再填充 3 字节而Order2把int放前面两个char紧挨着只填充 2 字节。因此把占用空间大的成员放在前面可以减小结构体大小。#includestddef.h#includestdio.hintmain(){printf(a 的偏移%zu\n,offsetof(structExample,a));// 0printf(b 的偏移%zu\n,offsetof(structExample,b));// 4printf(结构体大小%zu\n,sizeof(structExample));// 8return0;}3. 共同体union共同体是一种特殊的复合数据类型它的所有成员共享同一块内存空间。共同体的大小等于最大成员的大小同一时刻只能有一个成员的值是有效的。3.1 定义与声明unionData{inti;floatf;charstr[20];};定义共同体后就可以用它来声明变量unionData data;data.i10;// 此时 data 中有效的是 int 成员data.f3.14;// 写入 float 会覆盖之前的 int 值3.2 共同体的大小共同体的大小等于最大成员的大小printf(%zu\n,sizeof(unionData));// 输出 20即最大成员 str 的大小3.3 结构体与共同体的区别3.3 结构体与共同体的区别对比项结构体 struct共同体 union内存分配各成员独立分配内存所有成员共享一块内存大小约等于各成员大小之和含对齐填充等于最大成员的大小成员有效性所有成员同时有效同一时刻只有一个成员有效典型用途描述一个对象的多个属性节省内存、类型转换、协议解析4. 典型应用场景4.1 结构体学生信息管理下面用一个更完整的案例综合运用结构体数组、结构体指针、函数传参和内存对齐等知识点实现一个简单的学生成绩管理系统#includestdio.h#includestring.h#defineMAX_STUDENTS50structStudent{charname[20];intage;floatscore;};// 录入一个学生信息通过指针修改结构体voidinputStudent(structStudent*stu){printf(请输入姓名);scanf(%s,stu-name);printf(请输入年龄);scanf(%d,stu-age);printf(请输入成绩);scanf(%f,stu-score);}// 打印一个学生信息voidprintStudent(conststructStudent*stu){printf(姓名%s年龄%d成绩%.1f\n,stu-name,stu-age,stu-score);}// 计算全班平均分floataverageScore(conststructStudentclass[],intn){floatsum0;for(inti0;in;i){sumclass[i].score;}returnn0?sum/n:0;}// 找出最高分学生返回其下标intfindTopStudent(conststructStudentclass[],intn){inttop0;for(inti1;in;i){if(class[i].scoreclass[top].score){topi;}}returntop;}intmain(){structStudentclass[MAX_STUDENTS];intcount0;// 录入 3 名学生for(inti0;i3;i){printf(录入第 %d 名学生\n,i1);inputStudent(class[i]);count;printf(\n);}// 打印所有学生printf( 学生名单 \n);for(inti0;icount;i){printStudent(class[i]);}// 统计信息printf(\n 统计信息 \n);printf(全班平均分%.1f\n,averageScore(class,count));inttopfindTopStudent(class,count);printf(最高分学生);printStudent(class[top]);printf(\n结构体大小%zu 字节\n,sizeof(structStudent));return0;}这个案例把前面学到的知识点串了起来结构体数组class[50]用来存储多个学生结构体指针作为函数参数避免拷贝整个结构体提高效率const修饰表示函数只读不改防止误修改数据sizeof查看结构体实际占用内存体会内存对齐的影响。运行效果示例录入第 1 名学生 请输入姓名张三 请输入年龄18 请输入成绩89.5 录入第 2 名学生 请输入姓名李四 请输入年龄20 请输入成绩92.5 录入第 3 名学生 请输入姓名王五 请输入年龄19 请输入成绩78 学生名单 姓名张三年龄18成绩89.5 姓名李四年龄20成绩92.5 姓名王五年龄19成绩78.0 统计信息 全班平均分86.7 最高分学生姓名李四年龄20成绩92.5 结构体大小28 字节4.2 共同体判断大小端共同体常用来检测系统的大小端字节序#includestdio.hunionEndian{inti;charc;};intmain(){unionEndian e;e.i1;if(e.c1){printf(小端序\n);}else{printf(大端序\n);}return0;}5. 总结今天主要掌握了结构体把不同类型的数据组合成一个整体成员各自独立占用内存共同体让多个成员共享内存节省空间但同一时刻只有一个成员有效结构体常用于描述对象的多个属性共同体常用于节省内存和底层数据处理结构体存在内存对齐机制计算大小时要注意填充字节。结构体和共同体是 C 语言中非常实用的复合类型后续在链表、文件读写、网络协议解析等场景中还会大量用到值得反复练习巩固。