c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??

目录
一,结构体的对齐规则
二,默认
三,例题讲解
1,例题一
2,例题二
3,例题三
4,例题四
四,为什么存在内存对齐?
五,修改默认对齐数
六,总结
一,结构体的对齐规则

1,结构体的第一个成员永远放在结构体起始位置偏移为0的地址
2,结构体从第二个成员,总是放在一个对齐数的整数倍数
对齐数 = 编译器默认的对齐数和变量自身大小的较小值
3,. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4,如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
二,默认
Linux没有默认对齐数
Vs默认对齐数为8
三,例题讲解 1,例题一
int main() { struct S1 { char c1; int i; char c2; }; printf("%d\n", sizeof(struct S1)); return 0; }

c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??
文章图片

c1在结构体起始位置偏移为0的地址。
i是第二个成员,总是放在一个对齐数的整数倍数,对齐数 = 编译器默认的对齐数和变量自身大小的较小值,i本身为4,Vs默认为8,我们选择从4开始,中间的全部丢弃。
c2本身为一个字节,相比于Vs8来说还是1。
由第三条规则可得:结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
最大对齐数位4,此时我们的内存在8,总大小为9,继续往后延申到达11,总大小为12,是4的整数倍,符合第三条规则。
int main() { struct S1 { char c1; //字节大小1 Vs为8 所以为1 int i; //字节大小4 Vs为8 所以为4 char c2; //字节大小1 Vs为8 所以为1 }; printf("%d\n", sizeof(struct S1)); return 0; }

c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??
文章图片

2,例题二
int main() { struct S2 { char c1; char c2; int i; }; printf("%d\n", sizeof(struct S2)); return 0; }

c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??
文章图片

c1在结构体起始位置偏移为0的地址。
c2是第二个成员,总是放在一个对齐数的整数倍数,对齐数 = 编译器默认的对齐数和变量自身大小的较小值,c2本身为1,Vs默认为8,我们选择从1开始。
i本身为4个字节,相比于Vs8来说还是4。
由第三条规则可得:结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
【c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??】最大对齐数位4,此时我们的内存在7,总大小为8,是4 的整数倍数,符合第三条规则。
int main() { struct S2 { char c1; //字节大小1 Vs为8 所以为1 char c2; //字节大小1 Vs为8 所以为1 int i; //字节大小4 Vs为8 所以为4 }; printf("%d\n", sizeof(struct S2)); return 0; }

c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??
文章图片

3,例题三
int main() { struct S3 { double d; char c; int i; }; printf("%d\n", sizeof(struct S3)); return 0; }

c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??
文章图片

d在结构体起始位置偏移为0的地址。
c是第二个成员,总是放在一个对齐数的整数倍数,对齐数 = 编译器默认的对齐数和变量自身大小的较小值,c2本身为1,Vs默认为8,我们选择从8开始。
i本身为4个字节,相比于Vs8来说还是4。但它必须从12开始,因为此时内存为9,要符合第二条规则。
由第三条规则可得:结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
最大对齐数位4,此时我们的内存在15,总大小为16,是8的整数倍,符合第三条规则。
int main() { struct S3 { double d; ///字节大小8 Vs为8 所以为8 char c; //字节大小1 Vs为8 所以为1 int i; //字节大小4 Vs为8 所以为4 }; printf("%d\n", sizeof(struct S3)); return 0; }

c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??
文章图片

4,例题四
int main() { struct S3 { double d; char c; int i; }; printf("%d\n", sizeof(struct S3)); struct S4 { char c1; // struct S3 s3; double d; }; printf("%d\n", sizeof(struct S4)); return 0; }

c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??
文章图片

c1在结构体起始位置偏移为0的地址。
由第四条规则可得嵌套的结构体对齐到自己的最大对齐数的整数倍处,S3最大整数倍数为8,所以我们必须从8开始,中间的丢弃,结构体S3本身为16,向后延申16到23.
d为8个字节,从24开始符合规则
由第三条规则可得:结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
最大对齐数位16,此时我们的内存在23,总大小为24,继续延申到31,总大小为32,是16的整数倍,符合第三条规则。
int main() { struct S3 { double d; //字节大小8 Vs为8 所以为8 char c; //字节大小1 Vs为8 所以为1 int i; //字节大小4 Vs为8 所以为4 }; printf("%d\n", sizeof(struct S3)); struct S4 { char c1; //字节大小1 Vs为8 所以为1 struct S3 s3; //字节大小16 double d; //字节大小8 Vs为8 所以为8 }; printf("%d\n", sizeof(struct S4)); return 0; }

四,为什么存在内存对齐?
大部分的参考资料都是如是说的:
1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能 在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。 总体来说: 结构体的内存对齐是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起。
struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; };

S1和S2类型的成员一模一样,但是S1和S2所占空间的大小有了一些区别。
五,修改默认对齐数
#include #pragma pack(8)//设置默认对齐数为8 struct S1 { char c1; int i; char c2; }; #pragma pack()//取消设置的默认对齐数,还原为默认 #pragma pack(1)//设置默认对齐数为8 struct S2 { char c1; int i; char c2; }; #pragma pack()//取消设置的默认对齐数,还原为默认 int main() { //输出的结果是什么? printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); return 0; }

c语言|??图解C语言结构体对齐,保姆级教学,建议收藏??
文章图片

六,总结
既然看到这里了,求赞求评论求收藏,你们的三连是我进步的最大动力。关注我,一起学习进步

    推荐阅读