C/C++ 中的 struct:从 C 到 C++ 的进化

发布时间:2026/7/31 2:55:47
C/C++ 中的 struct:从 C 到 C++ 的进化 C/C 中的 struct从 C 到 C 的进化struct 就是用来把一堆数据打包到一起的东西。C 和 C 里它长不一样一个一个说。1. C 语言中的 struct——纯粹的数据打包工具C 语言没有 class没有面向对象那套东西。struct就是把多个不同类型的变量组合成一个整体的自定义数据类型。1.1 基本语法struct结构体名{类型 成员1;类型 成员2;// ...};// 分号不能少举个例子structStudent{charname[20];intage;floatscore;};1.2 定义变量C 语言里定义结构体变量时struct关键字不能省structStudents1;// 必须带 struct嫌麻烦可以用typedef重命名typedefstructStudent{charname[20];intage;floatscore;}Stu;Stu s1;// 现在不用写 struct 了1.3 初始化按成员顺序依次赋值structStudents1{张三,20,90.5};1.4 访问与赋值用.操作符s1.age21;相同类型的结构体变量可以整体赋值structStudents2s1;// 整体拷贝1.5 传参传值拷贝整个结构体浪费空间和时间。传址传指针只传 4/8 个字节更快。voidprintStudent(conststructStudent*s){printf(%s, %d\n,s-name,s-age);}所以结构体传参最好传指针。1.6 C 语言 struct 的局限不能有成员函数只能放函数指针没有访问控制所有成员对外可见没有继承没有多态不能有静态成员2. C 中的 struct——升级版C 的struct是 C 的struct的超集。几乎所有合法的 C 结构体在 C 里也合法。C 的struct和class几乎一样唯一区别是默认访问权限特性structclass默认成员访问权限publicprivate默认继承方式public继承private继承2.1 可以有成员函数structStudent{charname[20];intage;voidprint(){coutname, ageendl;}};2.2 可以有构造函数和析构函数structStudent{string name;intage;Student(conststringn,inta):name(n),age(a){}~Student(){/* 清理资源 */}};2.3 可以有访问控制structStudent{private:intid;// private外部不能访问public:string name;intage;};2.4 可以继承structPerson{string name;};structStudent:Person{// 默认 public 继承intgrade;};2.5 定义变量不用写 structC 里直接用结构体名Student s1;// 不用写 struct2.6 空结构体C 允许空结构体大小为 1 字节structEmpty{};sizeof(Empty);// 13. 内存对齐——为什么 sizeof 比你算的大这是 C 和 C 里最容易踩的坑。3.1 什么是对齐CPU 读取内存不是逐字节读的而是按块读的32 位系统一次 4 字节64 位一次 8 字节。如果数据没对齐到合适的边界CPU 可能需要两次读取再加拼接效率大打折扣。有些 ARM 平台甚至直接崩溃。所以编译器会在成员之间插入空白字节padding用空间换时间。3.2 对齐规则规则一第一个成员放在偏移量为 0 的位置。规则二其他成员的起始偏移量必须是其自身大小的整数倍。规则三结构体总大小必须是最宽成员大小的整数倍。看例子structTest{chara;// 1 字节intb;// 4 字节charc;// 1 字节};直觉1 4 1 6 字节。实际sizeof(struct Test)通常是12 字节。手动算一遍步骤操作偏移量1放char a02放int b对齐要求 4跳到偏移 44~73放char c84结构体总大小必须是最大成员int4的整数倍8→12补 3 字节布局[a][_][_][_][b][b][b][b][c][_][_][_]3.3 优化技巧大类型放前面同样的成员换个顺序能省空间structBad{chara;// 偏移 0intb;// 偏移 4~7charc;// 偏移 8总大小 12};structGood{intb;// 偏移 0~3chara;// 偏移 4charc;// 偏移 5总大小 86 对齐到 8};把大的成员放前面小的放后面能减少填充。3.4 #pragma pack——强行改对齐用#pragma pack(n)可以控制对齐#pragmapack(1)// 1 字节对齐取消所有填充structPacked{chara;intb;charc;};// 大小 141 6#pragmapack()// 恢复默认注意#pragma pack()直接恢复默认不保存旧值可能污染后续代码。安全写法是用push/pop#pragmapack(push,1)structProtocol{charhead;intlen;shortcmd;};// 大小 1427#pragmapack(pop)使用场景日常开发默认对齐就行网络报文、串口通信、驱动、文件存储必须pack(1)精确控制布局4. 位段位域——按 bit 分配位段让你按二进制位分配空间而不是按字节。structFlags{unsignedintflag1:1;// 占 1 bitunsignedintflag2:1;// 占 1 bitunsignedintflag3:3;// 占 3 bitsunsignedintdata:8;// 占 8 bits};适合节省内存的场景比如协议头、状态标志。注意位段的跨平台性不好不同编译器对位段的内存布局处理不一样。嵌入式里用得比较多。5. 柔性数组——C99 的变长数组C99 引入的特性结构体的最后一个成员可以是一个没有指定大小的数组。structflex{intlen;intdata[];// 柔性数组成员不占结构体大小};5.1 特点柔性数组前面必须至少有一个其他成员sizeof不包含柔性数组的大小需要用malloc动态分配额外空间structflex*pmalloc(sizeof(structflex)10*sizeof(int));p-len10;for(inti0;i10;i){p-data[i]i;}5.2 为什么用柔性数组而不是指针对比两种方案方案一柔性数组structS{intn;intarr[];};// 一次 malloc一次 free方案二指针structS{intn;int*arr;};// 两次 malloc两次 free柔性数组的好处内存释放更方便一次free全释放不容易漏访问速度更快数据在结构体后面连续存放缓存友好6. 完整示例C 语言版本——学生管理系统纯数据#includestdio.h#includestring.htypedefstructStudent{charname[20];intage;floatscore;}Student;// 传指针省拷贝voidprintStudent(constStudent*s){printf(姓名: %s, 年龄: %d, 成绩: %.1f\n,s-name,s-age,s-score);}intmain(){Student s1{张三,20,90.5};Student s2s1;// 整体赋值s2.age21;strcpy(s2.name,李四);printStudent(s1);printStudent(s2);// 内存对齐演示structTest{chara;intb;charc;};printf(sizeof(Test) %zu\n,sizeof(structTest));// 通常是 12return0;}C 版本——带成员函数和构造#includeiostream#includestring#includevectorusingnamespacestd;structStudent{private:intid;// private外部不能直接访问public:string name;intage;vectorfloatscores;// C 可以用 STL// 构造函数Student(conststringn,inta,intid):name(n),age(a),id(id){}// 成员函数voidaddScore(floats){scores.push_back(s);}floatgetAverage()const{if(scores.empty())return0;floatsum0;for(floats:scores)sums;returnsum/scores.size();}voidprint()const{coutID: id, 姓名: name, 年龄: ageendl;cout平均分: getAverage()endl;}};// 继承structGraduateStudent:Student{// 默认 public 继承string researchTopic;GraduateStudent(conststringn,inta,intid,conststringtopic):Student(n,a,id),researchTopic(topic){}};intmain(){Students1(张三,20,1001);s1.addScore(85.5);s1.addScore(92.0);s1.addScore(78.5);s1.print();GraduateStudentgs(李四,25,2001,人工智能);gs.addScore(95.0);gs.print();cout研究方向: gs.researchTopicendl;// C struct 和 class 的唯一区别默认 publicstructPoint{intx,y;};// 默认 publicclassPoint2{intx,y;};// 默认 private编译报错return0;}7. 总结与建议场景用什么纯 C 项目只打包数据structC 项目简单的 POD 类型Plain Old DatastructC 项目有复杂行为、需要封装class需要和 C 代码交互struct两边兼容网络协议、文件格式解析struct#pragma pack(1)一句话总结C 语言struct就是数据容器简单直接C 语言struct约等于class区别只是默认publicvsprivate内存对齐是编译器干的活但你知道规则才能写出内存紧凑的代码柔性数组是 C 语言里处理变长数据的利器比指针方案更优雅