【C】结构体的内存对齐

张开发
2026/4/10 15:30:55 15 分钟阅读

分享文章

【C】结构体的内存对齐
内存对齐的作用1、不是所有的硬件平台都能访问任意地址上的任意数据某些硬件平台只能在某些地址处取特定类型的数据否则抛出硬件异常可参考【培训】考试试题1。2、数据结构尤其是栈应该尽可能的在自然边界上对齐可以让处理器尽可能的只做一次访问。char、int、double等基础变量类型的自然对齐原则变量地址能够被类型大小整除数组的自然对齐原则按数组元素的类型处理struct自然对齐的3个原则1、结构体变量的起始地址能够被其最宽成员类型大小整除第一个成员地址一定等于起始地址2、结构体每个成员相对于起始地址的偏移能够被其自身类型大小整除如果不能则在前一个成员后面补充字节3、结构体总体大小能够被最宽成员类型大小整除如不能则在后面补充字节注意如果程序中有#pragma pack(n)预编译指令对于第1和第3原则取n和结构体内最宽成员类型大小中的最小值作为对齐标准用“对齐标准”替换原则中的“最宽成员类型大小”。对于第2原则取n和每个成员自身类型大小中的最小值作为对齐标准用“对齐标准”替换原则中的“自身类型大小”。举例struct S { char c; int i; };sizeof(struct S)为8struct S { double d; char c; int i; short s; };sizeof(struct S)为24#pragma pack(1) struct S { char c; int i; }; #pragma pack()sizeof(struct S)为5#pragma pack(4) struct S { char c[3]; short s; }; #pragma pack()sizeof(struct S)为6char c[3]虽然整体大小占3直接但是每个成员还是1字节结构体里最大成员单位是2字节整个结构体对齐到2的整数倍。如果c是一个3字节大小结构体结果不变结构体大小不属于成员类型。结构体字节对齐意义将一个结构体数据拷贝到一段字节缓存中用偏移去取数据如果结构体字节不对齐就会出问题可以用#pragma pack(1)去修饰。在使用这种方法的时候对于结构体里uint8_t可以直接访问但是对于uint16_t及以上必须用单字节拆解拼合的方式去读写不能直接使用寄存器地址之类的必须对齐使用。补充如果编译优化等级不是-O0一般的编译器会优化这种非对齐结构体实际汇编码是单字节访问同时对于奇地址不能强转强转的话优化效果会消失。总结就是非对齐结构体的变量可以直接使用但是不能把一个奇地址赋值或强转成一个偶地址指针再使用这个指针处理变量。union自然对齐的3个原则1、联合体变量的起始地址能够被其最宽成员类型大小整除2、联合体每个成员相对于起始地址的偏移都是03、联合体总体大小能够被最宽成员类型大小整除如不能则在后面补充字节union U { double d; int i[5]; char c; };sizeof(union U)为24堆栈内存对齐全局或静态数据直接按自然对齐即可堆栈区的起始地址由编译器强制对齐到8字节启动或者链接文件中配置UCOS上线程栈没有对齐到8字节就会出现浮点数打印异常AAPCSProcedure Call Standard for the Arm Architecture规范要求栈在任何时候都保持4字节对齐在函数调用入口点保持8字节对齐。这意味着在调用函数时栈指针必须是8字节对齐的。但是如果某个空函数没有用栈空间编译器会优化成4字节对齐减少压栈函数调用等于压栈函数返回等于出栈压栈入栈弹栈出栈。中断入口函数的栈顶地址可以通过SCB有的是NVIC中的STKALIGN置位来对齐到8字节如果压栈时减4字节出栈时会自动再加4字节栈区是由高到低向下生长。局部变量的对齐原则为自然对齐AC5下所有局部变量地址强制对齐到4字节

更多文章