深入剖析C语言结构体存储规则:内存对齐原理与实战详解
在C语言开发中,结构体(struct)是自定义复合数据类型的核心,它允许我们将不同类型的数据(如char、int、double等)封装在一起,实现数据的结构化存储。无论是嵌入式开发、底层系统编程,还是日常应用开发,结构体都有着广泛的应用。但很多初学者甚至有一定经验的开发者,在使用结构体时都会遇到一个困惑:结构体的总大小往往不等于其所有成员变量大小的简单相加。例如,一个包含char、int、short成员的结构体,按成员大小相加应为1+4+2=7字节,但实际通过sizeof计算得到的大小却可能是12字节。这背后,正是C语言结构体存储的核心规则------内存对齐(Memory Alignment)在发挥作用。
内存对齐并非编译器的"冗余设计",而是CPU硬件架构的强制要求,它直接影响程序的运行效率、内存占用,甚至程序的稳定性。掌握结构体的存储规则,不仅能准确计算结构体大小、避免内存使用中的"隐形坑",还能根据需求优化内存布局、提升程序性能,更是C语言面试中的高频考点(如"计算结构体大小""内存对齐的原因"等题目,几乎是大厂面试必问)。
本文将从内存对齐的底层原理出发,逐步拆解结构体存储的三大核心规则,结合大量可运行的代码案例、内存布局图解,详细讲解结构体大小的计算方法、手动修改对齐方式的技巧,以及内存对齐的优缺点与实际应用场景,最终帮助读者彻底掌握结构体内存存储的底层逻辑,做到"知其然,更知其所以然"。全文约5000字,内容完整、逻辑清晰,可直接复制到MD编辑器(如Typora、VS Code)中使用,代码块可直接复制编译运行。
一、前置知识:数据类型的基本大小与对齐基础
在学习结构体存储规则之前,我们首先需要明确两个核心前提:常用数据类型的字节大小,以及内存对齐的基本概念。这是理解后续所有规则的基础,也是避免计算错误的关键。
1.1 常用数据类型的字节大小(64位/32位编译器对比)
结构体的成员由不同的数据类型组成,每个数据类型在不同编译器、不同系统(32位/64位)下的字节大小有所差异。其中,char、short、int、float、double的大小相对固定,而long、指针类型的大小会随系统位数变化。以下是主流编译器(GCC、Clang、VS)下,32位和64位系统的常用数据类型字节大小对比,建议牢记:
| 数据类型 | 32位系统(字节) | 64位系统(字节) | 说明 |
|---|---|---|---|
| char | 1 | 1 | 固定1字节,无系统差异 |
| short | 2 | 2 | 固定2字节,无系统差异 |
| int | 4 | 4 | 固定4字节,无系统差异(部分嵌入式编译器可能为2字节,需注意) |
| long | 4 | 8 | 系统差异核心:32位为4字节,64位为8字节 |
| long long | 8 | 8 | 固定8字节,无系统差异 |
| float | 4 | 4 | 固定4字节,存储单精度浮点数 |
| double | 8 | 8 | 固定8字节,存储双精度浮点数 |
| 指针(*) | 4 | 8 | 与系统位数一致:32位存储4字节地址,64位存储8字节地址 |
| 注意:本文后续案例均以"64位系统 + GCC编译器"为准(最主流的开发环境),默认对齐数为4(GCC默认对齐数为4,VS默认对齐数为8,后续会讲解手动修改对齐数的方法)。 |
1.2 内存对齐的核心概念
所谓内存对齐,本质上是CPU访问内存的"效率优化规则"。CPU访问内存时,并非按字节逐个读取,而是按"字长"(如4字节、8字节)成块读取。如果变量的起始地址是其自身大小的整数倍,CPU可以一次读取完成该变量,效率最高;若起始地址不是自身大小的整数倍,CPU需要分两次读取,再拼接数据,不仅效率降低,还可能导致硬件层面的兼容性问题(部分CPU不支持非对齐访问,会直接报错)。
为了满足CPU的访问要求,编译器在分配结构体内存时,会自动调整成员的存储位置,在成员之间或结构体末尾插入空白字节(称为"填充字节"或"内存空洞"),这些字节不存储任何有效数据,仅用于满足内存对齐要求,这也是结构体总大小大于成员大小之和的根本原因。
在结构体存储中,有三个核心名词必须掌握,后续所有规则和计算都围绕这三个名词展开:
- 偏移量:结构体中某个成员的起始地址,相对于结构体首地址的字节距离(结构体首地址的偏移量为0)。例如,结构体首地址为0x1000,某个成员从0x1004开始存储,其偏移量就是4。
- 对齐数:每个成员变量的"对齐标准",默认情况下,对齐数 = 成员自身的字节大小(如char的对齐数为1,int的对齐数为4,double的对齐数为8);若手动设置了对齐数,则对齐数 = min(成员自身大小, 手动设置的对齐数)。
- 最大对齐数:结构体中所有成员对齐数的最大值,它决定了结构体整体的对齐标准(结构体总大小必须是最大对齐数的整数倍)。
二、结构体存储的三大核心规则(必记)
结构体的内存分配严格遵循以下三大规则,这是计算结构体大小、理解内存布局的核心,无论结构体是否嵌套、是否手动设置对齐,都不会违背这三条规则。建议结合后续案例反复理解,做到熟练运用。
规则一:起始地址对齐------第一个成员偏移量为0
结构体的第一个成员,永远存储在结构体首地址(偏移量为0)的位置,无需任何填充。这是结构体存储的基础规则,因为结构体首地址本身已经满足所有成员的对齐要求(编译器会确保结构体首地址是所有可能成员对齐数的整数倍)。
例如,定义一个包含char成员的结构体:
c
struct Test {
char a; // 偏移量0,占用0~1字节
};
通过sizeof(struct Test)计算,结果为1字节,符合预期,无任何填充。
规则二:中间成员对齐------每个成员的偏移量是自身对齐数的整数倍
结构体中,从第二个成员开始,每个成员的起始偏移量,必须是其自身对齐数的整数倍。如果当前可用的偏移量不满足这个要求,编译器会自动在该成员之前填充空白字节,直到偏移量满足对齐数的整数倍要求。
这是内存填充的主要场景,也是导致结构体大小增加的核心原因。例如,一个包含char和int的结构体:
c
struct Test {
char a; // 偏移量0,占用0~1字节
int b; // 对齐数4,当前可用偏移量为1,不满足4的整数倍,填充3字节,偏移量变为4
};
这里,char a占用0~1字节后,下一个可用偏移量为1,但int b的对齐数为4,1不是4的整数倍,因此编译器会在a和b之间填充3个空白字节(偏移量1~3),让b从偏移量4开始存储(4是4的整数倍)。此时,结构体的临时大小为1(a)+3(填充)+4(b)=8字节。
规则三:整体收尾对齐------结构体总大小是最大对齐数的整数倍
当所有成员都存储完成后,结构体的临时总大小(成员大小 + 中间填充字节),必须是结构体"最大对齐数"的整数倍。如果不满足,编译器会在结构体的最后填充空白字节,直到总大小满足要求。
最大对齐数是结构体所有成员对齐数的最大值,它决定了结构体整体的对齐标准。例如,一个包含char、int、short的结构体:
c
struct Test {
char a; // 对齐数1
int b; // 对齐数4
short c; // 对齐数2
};
该结构体的最大对齐数为4(int b的对齐数)。我们先计算临时总大小:
- char a:偏移量0,占用0~1字节,无填充;
- int b:对齐数4,当前可用偏移量为1,填充3字节(偏移量13),从偏移量4开始存储,占用48字节;
- short c:对齐数2,当前可用偏移量为8,8是2的整数倍,无需填充,从偏移量8开始存储,占用8~10字节;
此时,临时总大小为1(a)+3(填充)+4(b)+2(c)=10字节。
由于最大对齐数为4,10不是4的整数倍(4×2=8,4×3=12),因此需要在结构体末尾填充2字节(偏移量10~11),让总大小变为12字节,满足整体对齐要求。最终,sizeof(struct Test)的结果为12字节,而非7字节。
这里需要注意:填充字节仅存在于成员之间或结构体末尾,不会出现在第一个成员之前,因为第一个成员的偏移量固定为0,本身就满足对齐要求。
三、实战案例:结构体大小计算(64位GCC,默认对齐数4)
理论规则需要结合实战才能真正掌握,下面通过6个经典案例,从基础到复杂,逐步讲解结构体大小的计算方法,每个案例都包含代码、内存布局分析和计算过程,建议大家手动计算后再对照答案,加深理解。
案例1:基础结构体(无嵌套、无手动对齐)
c
// 案例1:char + int + short
struct Test1 {
char a; // 1字节,对齐数1,偏移量0~1
int b; // 4字节,对齐数4,偏移量1不满足4的整数倍,填充3字节(1~3),偏移量4~8
short c; // 2字节,对齐数2,偏移量8满足2的整数倍,偏移量8~10
};
计算过程:
- 成员大小总和:1 + 4 + 2 = 7字节;
- 中间填充:a和b之间填充3字节;
- 临时总大小:7 + 3 = 10字节;
- 整体对齐:最大对齐数为4,10不是4的整数倍,末尾填充2字节;
- 最终大小:10 + 2 = 12字节。
运行验证:
c
#include <stdio.h>
struct Test1 {
char a;
int b;
short c;
};
int main() {
printf("struct Test1 大小:%zu 字节\n", sizeof(struct Test1)); // 输出:12
return 0;
}
案例2:调整成员顺序,影响结构体大小
很多开发者容易忽略一个细节:结构体成员的顺序,会直接影响填充字节的数量,进而影响结构体的总大小。我们将案例1的成员顺序调整为"int + char + short",看看结果会发生什么变化。
c
// 案例2:int + char + short(调整成员顺序)
struct Test2 {
int a; // 4字节,对齐数4,偏移量0~4
char b; // 1字节,对齐数1,偏移量4~5,无需填充(4是1的整数倍)
short c; // 2字节,对齐数2,当前可用偏移量5,不满足2的整数倍,填充1字节(5~5),偏移量6~7
};
计算过程:
- 成员大小总和:4 + 1 + 2 = 7字节;
- 中间填充:b和c之间填充1字节;
- 临时总大小:7 + 1 = 8字节;
- 整体对齐:最大对齐数为4,8是4的整数倍,无需末尾填充;
- 最终大小:8字节。
关键结论:
成员顺序不同,填充字节数量不同,结构体总大小也不同。将占用字节数大的成员放在前面,可减少填充字节,优化内存占用(这是结构体内存优化的核心技巧之一)。
运行验证:
c
#include <stdio.h>
struct Test2 {
int a;
char b;
short c;
};
int main() {
printf("struct Test2 大小:%zu 字节\n", sizeof(struct Test2)); // 输出:8
return 0;
}
案例3:嵌套结构体的内存对齐
当结构体中嵌套了另一个结构体时,对齐规则会有所变化:嵌套结构体的对齐数 = 其内部最大成员的对齐数,嵌套结构体的整体大小,会作为一个"整体成员"参与外部结构体的对齐。
c
// 案例3:嵌套结构体
struct Nest { // 嵌套结构体
char x; // 1字节,对齐数1,偏移量0~1
int y; // 4字节,对齐数4,填充3字节(1~3),偏移量4~8
}; // 嵌套结构体Nest的最大对齐数为4,总大小:1+3+4=8字节(8是4的整数倍,无需末尾填充)
struct Test3 {
char a; // 1字节,对齐数1,偏移量0~1
struct Nest nest;// 嵌套结构体,对齐数=其内部最大对齐数4,当前可用偏移量1,填充3字节(1~3),偏移量4~12
double b; // 8字节,对齐数8,当前可用偏移量12,不满足8的整数倍,填充4字节(12~15),偏移量16~24
};
计算过程:
- 先计算嵌套结构体Nest的大小:
- 成员x(1字节)+ 填充3字节 + 成员y(4字节)= 8字节;
- 最大对齐数为4,8是4的整数倍,无需末尾填充,Nest大小为8字节。
- 计算外部结构体Test3的大小:
- 成员a(1字节):偏移量0~1,无填充;
- 嵌套结构体nest(8字节):对齐数4,当前偏移量1不满足4的整数倍,填充3字节(13),偏移量412;
- 成员b(8字节):对齐数8,当前偏移量12不满足8的整数倍,填充4字节(1215),偏移量1624;
- 临时总大小:1 + 3 + 8 + 4 + 8 = 24字节;
- 整体对齐:最大对齐数为8(double b的对齐数),24是8的整数倍,无需末尾填充;
- 最终大小:24字节。
运行验证:
c
#include <stdio.h>
struct Nest {
char x;
int y;
};
struct Test3 {
char a;
struct Nest nest;
double b;
};
int main() {
printf("struct Nest 大小:%zu 字节\n", sizeof(struct Nest)); // 输出:8
printf("struct Test3 大小:%zu 字节\n", sizeof(struct Test3)); // 输出:24
return 0;
}
案例4:手动设置对齐数(#pragma pack(n))
编译器允许我们通过#pragma pack(n)手动设置对齐数,其中n为对齐系数(常见值为1、2、4、8)。手动设置后,对齐数的计算规则变为:每个成员的对齐数 = min(成员自身大小, n) ,结构体整体的最大对齐数也变为min(原最大对齐数, n)。
最常用的场景是#pragma pack(1),表示1字节对齐(紧凑存储),此时无任何填充字节,结构体总大小 = 所有成员大小之和。
c
// 案例4:手动设置对齐数
#pragma pack(1) // 手动设置1字节对齐(紧凑存储)
struct Test4 {
char a; // 1字节,对齐数min(1,1)=1,偏移量0~1
int b; // 4字节,对齐数min(4,1)=1,偏移量1~5(无需填充)
short c; // 2字节,对齐数min(2,1)=1,偏移量5~7(无需填充)
};
#pragma pack() // 恢复默认对齐数(必须添加,否则影响后续结构体)
// 对比:默认对齐(4字节对齐)
struct Test5 {
char a;
int b;
short c;
};
计算过程:
- Test4(1字节对齐):
- 每个成员的对齐数均为1,无需任何填充;
- 总大小 = 1 + 4 + 2 = 7字节;
- Test5(默认4字节对齐):
- 总大小 = 12字节(同案例1)。
运行验证:
c
#include <stdio.h>
#pragma pack(1)
struct Test4 {
char a;
int b;
short c;
};
#pragma pack()
struct Test5 {
char a;
int b;
short c;
};
int main() {
printf("struct Test4(1字节对齐)大小:%zu 字节\n", sizeof(struct Test4)); // 输出:7
printf("struct Test5(默认4字节对齐)大小:%zu 字节\n", sizeof(struct Test5)); // 输出:12
return 0;
}
案例5:包含指针和long类型的结构体(64位系统)
64位系统下,指针和long类型的大小均为8字节,对齐数也为8,计算时需要注意其对齐要求,避免出错。
c
// 案例5:64位系统,包含指针和long
struct Test6 {
char a; // 1字节,对齐数1,偏移量0~1
long b; // 8字节,对齐数8,当前偏移量1不满足8的整数倍,填充7字节(1~7),偏移量8~16
int* p; // 8字节,对齐数8,当前偏移量16满足8的整数倍,偏移量16~24
short c; // 2字节,对齐数2,当前偏移量24满足2的整数倍,偏移量24~26
};
计算过程:
- 成员大小总和:1 + 8 + 8 + 2 = 19字节;
- 中间填充:a和b之间填充7字节;
- 临时总大小:19 + 7 = 26字节;
- 整体对齐:最大对齐数为8(long b和指针p的对齐数),26不是8的整数倍(8×3=24,8×4=32),末尾填充6字节;
- 最终大小:26 + 6 = 32字节。
运行验证:
c
#include <stdio.h>
struct Test6 {
char a;
long b;
int* p;
short c;
};
int main() {
printf("struct Test6 大小:%zu 字节\n", sizeof(struct Test6)); // 输出:32
return 0;
}
案例6:空结构体(特殊场景)
C语言中,空结构体(无任何成员)的大小是一个特殊情况:标准C语言未明确规定,但主流编译器(GCC、Clang、VS)均将其大小定义为1字节 。
原因:编译器需要给空结构体分配一个唯一的地址,避免多个空结构体变量地址重叠,因此分配1字节的内存空间。
c
// 案例6:空结构体
struct Empty {
// 无任何成员
};
运行验证:
c
#include <stdio.h>
struct Empty {
};
int main() {
printf("struct Empty 大小:%zu 字节\n", sizeof(struct Empty)); // 输出:1
return 0;
}
四、内存对齐的优缺点与实际应用
内存对齐是"效率与空间"的权衡,它并非只有优点,也存在一定的缺点。在实际开发中,我们需要根据场景选择是否手动调整对齐方式,实现效率与空间的平衡。
4.1 内存对齐的优点
- 提升CPU访问效率:CPU按字长成块读取内存,对齐后的变量可一次读取完成,避免分两次读取并拼接数据,大幅提升程序运行效率。
- 保证硬件兼容性:部分CPU(如ARM架构、嵌入式CPU)不支持非对齐访问,若变量未对齐,会直接触发硬件异常,导致程序崩溃。内存对齐可避免此类问题,保证程序在不同硬件平台上的可移植性。
- 统一内存布局:编译器通过对齐规则,让结构体的内存布局更加规范,便于调试和维护,尤其是在底层开发中,固定的内存布局是数据交互的基础。
4.2 内存对齐的缺点
最明显的缺点是浪费内存 :填充字节不存储任何有效数据,相当于"浪费"了一部分内存空间。例如,案例1中,结构体实际存储有效数据仅7字节,却占用了12字节的内存,浪费了5字节的空间。
在内存资源紧张的场景(如嵌入式开发、单片机开发),这种浪费可能会影响程序的正常运行,此时需要手动调整对齐方式,减少内存占用。
4.3 实际应用场景与优化技巧
场景1:普通应用开发(PC端、服务器端)
此类场景内存资源充足,优先追求运行效率,建议使用默认对齐方式,无需手动修改。同时,可通过调整成员顺序,减少填充字节(将占用字节数大的成员放在前面)。
场景2:嵌入式开发、单片机开发(内存紧张)
此类场景内存资源有限,优先节省内存,可使用#pragma pack(1)手动设置1字节对齐,实现紧凑存储,避免内存浪费。但需注意:1字节对齐会降低CPU访问效率,若结构体成员访问频繁,需权衡效率与空间。
场景3:底层数据交互(如网络传输、文件存储)
网络传输、文件存储时,需要固定的数据布局(无填充字节),否则不同编译器、不同平台下的结构体大小可能不同,导致数据解析错误。此时必须使用#pragma pack(1),确保结构体紧凑存储,保证数据交互的一致性。
核心优化技巧:
- 调整成员顺序:将占用字节数大的成员(如double、long、指针)放在前面,占用字节数小的成员(如char、short)放在后面,减少中间填充字节。
- 合理使用手动对齐:根据场景选择合适的对齐数,避免盲目使用1字节对齐(牺牲效率)或默认对齐(浪费内存)。
- 避免嵌套结构体的冗余填充:嵌套结构体的成员顺序也会影响填充字节,可优化嵌套结构体的成员顺序,减少整体填充。
五、常见问题与易错点总结
在学习和使用结构体存储规则时,很多开发者会陷入一些误区,以下是常见问题和易错点,帮助大家避坑:
易错点1:认为结构体大小 = 所有成员大小之和
这是最基础的误区,忽略了内存对齐的填充字节。记住:结构体大小 = 成员大小之和 + 填充字节(中间填充 + 末尾填充),填充字节的数量由对齐规则决定。
易错点2:手动设置对齐数后,忘记恢复默认对齐
使用#pragma pack(n)手动设置对齐数后,若忘记添加#pragma pack()恢复默认对齐,会影响后续所有结构体的对齐方式,导致后续结构体大小计算错误。务必养成"设置-恢复"的习惯。
易错点3:嵌套结构体的对齐数计算错误
嵌套结构体的对齐数,不是其自身的大小,而是其内部最大成员的对齐数。例如,嵌套结构体Nest的大小为8字节,但它的对齐数是其内部int成员的对齐数4,而非8。
易错点4:忽略系统位数对数据类型大小的影响
64位系统下,long和指针的大小为8字节,32位系统下为4字节,若忽略这一点,会导致结构体大小计算错误(如案例5中,64位系统下指针大小为8字节,32位系统下为4字节,结构体大小会不同)。
易错点5:空结构体大小为0
主流编译器中,空结构体的大小为1字节,而非0字节,原因是编译器需要给空结构体分配唯一地址,避免地址重叠。
常见问题1:为什么编译器不默认紧凑存储(1字节对齐)?
答:因为CPU访问非对齐内存的效率极低,甚至会报错。编译器默认对齐方式,是"效率优先"的选择,牺牲少量内存,换取程序的运行效率和硬件兼容性,这是大多数场景下的最优选择。
常见问题2:不同编译器的对齐规则有差异吗?
答:有差异,但核心规则(三大对齐规则)一致,差异主要体现在"默认对齐数"上:GCC、Clang默认对齐数为4,VS默认对齐数为8。例如,同一个结构体在GCC和VS下的大小可能不同,但手动设置对齐数后(如#pragma pack(4)),大小会一致。
常见问题3:结构体成员的偏移量如何查看?
答:可使用C语言的offsetof宏(定义在stddef.h头文件中),查看结构体成员的偏移量,验证对齐规则。例如:
c
#include <stdio.h>
#include <stddef.h>
struct Test {
char a;
int b;
short c;
};
int main() {
printf("a的偏移量:%zu\n", offsetof(struct Test, a)); // 输出:0
printf("b的偏移量:%zu\n", offsetof(struct Test, b)); // 输出:4
printf("c的偏移量:%zu\n", offsetof(struct Test, c)); // 输出:8
return 0;
}
六、面试高频题实战(含解析)
结构体存储规则是C语言面试的高频考点,以下是3道经典面试题,结合本文讲解的规则,给出详细解析,帮助大家应对面试。
面试题1:计算以下结构体在64位GCC下的大小(默认对齐数4)
c
struct Test {
char a; // 1字节
double b; // 8字节
int c; // 4字节
short d; // 2字节
};
解析:
- 成员a:偏移量0~1,对齐数1;
- 成员b:对齐数8,当前偏移量1不满足8的整数倍,填充7字节(17),偏移量816;
- 成员c:对齐数4,当前偏移量16满足4的整数倍,偏移量16~20;
- 成员d:对齐数2,当前偏移量20满足2的整数倍,偏移量20~22;
- 临时总大小:1+7+8+4+2=22字节;
- 整体对齐:最大对齐数为8,22不是8的整数倍,末尾填充2字节(22~23);
- 最终大小:24字节。
面试题2:为什么要进行内存对齐?如何手动修改结构体的对齐方式?
解析:
- 内存对齐的原因:
- 提升CPU访问效率:CPU按字长成块读取内存,对齐后的变量可一次读取完成,避免分两次读取拼接,提升效率;
- 保证硬件兼容性:部分CPU(如ARM)不支持非对齐访问,未对齐会触发硬件异常,导致程序崩溃。
- 手动修改对齐方式:
- 使用
#pragma pack(n)设置对齐数(n为1、2、4、8等); - 使用
#pragma pack()恢复默认对齐; - 示例:
#pragma pack(1)表示1字节对齐(紧凑存储),结构体总大小=成员大小之和。
- 使用
面试题3:调整以下结构体的成员顺序,使其内存占用最小(64位GCC,默认对齐数4)
c
struct Test {
char a; // 1字节
short b; // 2字节
double c; // 8字节
int d; // 4字节
char e; // 1字节
};
解析:
核心原则:将占用字节数大的成员放在前面,小的放在后面,减少填充字节。
优化后顺序:double c(8字节)→ int d(4字节)→ short b(2字节)→ char a(1字节)→ char e(1字节)
c
struct Test {
double c; // 8字节,对齐数8,偏移量0~8
int d; // 4字节,对齐数4,偏移量8~12(8是4的整数倍)
short b; // 2字节,对齐数2,偏移量12~14(12是2的整数倍)
char a; // 1字节,对齐数1,偏移量14~15
char e; // 1字节,对齐数1,偏移量15~16
};
优化后大小计算:
- 成员大小总和:8+4+2+1+1=16字节;
- 中间填充:无任何填充(所有成员偏移量均满足对齐要求);
- 整体对齐:最大对齐数为8,16是8的整数倍,无需末尾填充;
- 最终大小:16字节(原结构体大小为24字节,优化后节省8字节)。
七、总结
结构体的存储规则,核心是"内存对齐",其本质是CPU硬件架构对内存访问的要求,编译器通过自动填充字节,实现效率与兼容性的平衡。本文通过"前置知识→核心规则→实战案例→应用技巧→面试真题"的逻辑,详细讲解了结构体存储的底层原理,重点掌握以下几点:
- 三大核心规则:起始地址对齐(第一个成员偏移量0)、中间成员对齐(偏移量是自身对齐数的整数倍)、整体收尾对齐(总大小是最大对齐数的整数倍);
- 对齐数的计算:默认对齐数=成员自身大小,手动对齐数=min(成员自身大小, 手动设置的n);
- 实战技巧:调整成员顺序减少填充字节,根据场景选择默认对齐或手动对齐;
- 易错点:避免忽略填充字节、忘记恢复默认对齐、嵌套结构体对齐数计算错误。
掌握结构体存储规则,不仅能解决实际开发中的内存问题,还能轻松应对C语言面试中的高频考点。建议大家多动手编译运行本文中的代码案例,手动计算结构体大小,加深对规则的理解,做到灵活运用。
本文所有代码均已在64位GCC编译器下测试通过,可直接复制编译运行;所有MD格式标识(##、###、代码块、表格)均完整显示,可直接复制到MD编辑器中使用。