#写在开头, 编译器的对齐值为8byte
如题, 首先说一下为什么需要字节对齐, 这个似乎有些浪费空间, 那就先上一下比较书面的解释:
1. 平台要求, 某些CPU只能访问地址为偶数的内存地址, 这个时候你把数据放在奇数地址, 就会报错
2. 性能要求, 这个跟寄存器有关系, 有些数据不对齐的话需要使用寄存器的次数会增多
说点通俗的, 就像一本书, 每页都有字, 但是不一定要写满; 写太满了, 没有章法, 看不了.
下面, 先说明三个概念:
1. 自身对齐值: 比如, short为2byte, double为8byte
2. 指定对齐值: 编译器自己决定的; 或者程序员自己决定的
1 #pragma pack (n) 2 *** 3 *** 4 #pragma pack ()
那么2, 3行内部的数据指定对齐值便是n
3. 有效对齐值: 上面两个值的最小值
对齐的规则呢:
1. 数据本身存储的地址应该是, 数据本身有效对齐值的整数倍
2. 对于结构体而言, 由于1这条规则, 以及有结构体数组的存在, 所以结构体本身的有效对齐值为内部 数据有效对齐值的最大值.
这么说还是比较抽象, 我们上点例子
1 struct A { 2 char a; 3 int b; 4 };
首先a的自身对齐值为1, 制定对齐值为8, 那么有效对齐值为min{1, 8} = 1
0x0 | 0x1 | 0x2 | 0x3 |
a |
接下来, 需要把b放进去, 他的地址应该时自身有效对齐为min{4, 8} = 4, 所以0x1, 0x2, 0x3都存不了,得从0x4开始存储
0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 |
a | b | b | b | b |
如上, 很简单对吧, 下面讲一下规则2, 给结构体A加个char c
1 struct A { 2 char a; 3 int b; 4 char c; 5 };
那么有趣的事情就会发生, a的有效对齐值为1, b的为4, c的为1, 那么A的就是4, 所以c的后面3个单元会被占用, 想一想, 如果不这样补齐的话, 你想想对于结构体数组的第二个元素的b他的地址就不是4的整数倍了.
0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xA | 0xB |
a | b | b | b | b | c |
所以, 究其根本, 所有的一切都是为了满足第1条规则.
那么再来如果把b换成double类型的呢?
1 struct A { 2 char a; 3 double b; 4 char c; 5 };
那么a的有效对齐值为1, b为8, c为1, A为8, 那么整体应该占24byte
0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xA | 0xB | 0xC | 0xD | 0xE | 0xF | 0x10 | 0x11 | 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 |
a | b | b | b | b | b | b | b | b | c |
光说不练假把式, 我们上几道题目
1 #pragma pack (4) 2 struct S1 { 3 char a; 4 double b; 5 }; 6 7 struct S2 { 8 char a; 9 S1 s; 10 char b; 11 char c; 12 }; 13 14 struct S3 { 15 int a; 16 char b[10]; 17 }; 18 #pragma pack ()
注意指定对齐值被改为了4, 拿自己的IDE试试吧!
有问题, 请联系760521757@qq.com