前言:
在上一张讲解了结构体的基本知识,在本章深入讲解一下结构体。
如内存对齐,传参,实现尾段。
首先提一个问题吧,如下的代码结果输出是多少?
#include <stdio.h>
struct s1
{
char name;
int id;
char xue;
}s1;
struct s2
{
char e1;
char e2;
int e3;
}s2;
int main()
{
printf("%zd\n", sizeof(s1));
printf("%zd\n", sizeof(s2));
return 0;
}
6,6???
nonono。
大家是如何计算的了,是char是一个字节,int是4个字节,是所以相加一共6个字节嘛?
正确的答案如下:
这就需要本章的知识了。
结构体内存对齐
对齐规则
char
:1字节对齐short
:2字节对齐int
:通常为4字节对齐,但在某些平台可能为2字节对齐。double
:8字节对齐。
-
结构体对齐
四大黄金法则
-
首地址规则:结构体首地址 = max(成员对齐要求) 的整数倍
-
成员偏移规则:成员偏移量 = min(成员大小, 当前对齐值) 的整数倍
-
整体大小规则:结构体总大小 = max(成员对齐要求) 的整数倍
-
嵌套结构规则:子结构体的对齐值取其自身最大对齐值
-
问题分析:
struct Example
{
char c1;
int n;
char c2;
}; // 总大小=12字节
c1在0的位置储存。
n不在1的位置储存,因为法则二中说了,偏移量是当前的的整数倍,所以向下浪费3个大小,n储存在4的位置,占4个字节。
c2储存在8的位置,结束了嘛?
没有。根据法则三总大小是max的整数倍,max也就是4,int的字节大小,所以继续向下浪费3各大小。
总大小是12。
大家可以分析一下s2是如何储存的了。
s1与s2的区别是顺序换了一下
struct s2
{
char e1;
char e2;
int e3;
}s2;
分析如下:
0,1分别储存e1,e2
2不是4的整数倍,所以向下浪费2个字节,在储存int的四个字节,在分析它的储存值是max的整数倍嘛,欧克是滴
答案是8!!
为什莫存在内存对齐
-
平台(移植性)原因:
不是所有的硬件平台都能够访问任意地址上的任意数据。例如:特定的硬件平台只允许在特定地址获取特定类型的数据,否则会导致异常情况。 -
性能原因:
若访问未对齐的内存,将会导致 CPU 进行两次内存访问,并且要花费额外的时钟周期来处理对齐及运算。而本身就对齐的内存仅需要一次访问就可以完成读取动作。
修改默认对齐数
在C语言中,修改默认对齐数通常通过编译器特定的预处理指令实现。以下是详细步骤和注意事项:
方法:使用 #pragma pack(n)
-
设置对齐数:
#pragma pack(n) // 将默认对齐数设置为n字节(n通常为1, 2, 4, 8等)
此指令后的结构体将按照
n
字节对齐,直到遇到新的#pragma pack
指令。 -
恢复默认对齐:
#pragma pack() // 恢复编译器默认对齐方式
-
保存并恢复原有设置(推荐):
#pragma pack(push, n) // 保存当前对齐状态,并设置新对齐数为n // 定义需要特殊对齐的结构体 #pragma pack(pop) // 恢复之前保存的对齐状态
示例:
#include <stdio.h>
#pragma pack(push, 1) // 保存当前对齐,设置对齐数为1
struct Example {
char a; // 1字节
int b; // 4字节(原本可能需要对齐到4,现按1字节对齐)
double c; // 8字节(原本可能需要对齐到8,现按1字节对齐)
};
#pragma pack(pop) // 恢复原有对齐
int main() {
printf("结构体大小: %zu\n", sizeof(struct Example)); // 输出:1 + 4 + 8 = 13(无填充)
return 0;
}
注意事项
-
编译器差异:
-
#pragma pack
是MSVC和GCC等主流编译器支持的指令,但属于编译器扩展,不属于标准C。 -
GCC还支持
__attribute__((packed))
对单个结构体取消对齐:struct Example { char a; int b; double c; } __attribute__((packed));
-
-
性能影响:
-
减少对齐数可能降低内存占用,但可能导致未对齐内存访问,影响性能(尤其在硬件要求严格对齐的平台上)。
-
-
可移植性:
-
依赖编译器指令的代码可能在不同平台间表现不一致,建议封装平台相关代码并使用条件编译。
-
总结:
本章讲解结构体的内存对齐,对齐值,和修改对齐数。
如果有所帮助的请关注一下,我们下章再见!!!