什么是字节对齐?
现代计算机中内存空间都是按照 byte 划分的, 从理论上讲似乎对任何类型的变量的访问可以从任何地址开始, 但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问, 这就需要各种类型数据按照一定的规则在空间上排列, 而不是顺序的一个接一个的排放, 这就是对齐.
为什么要字节对齐?
字节是否对齐关系到 CPU 访问数据时的效率问题, 假设一个 CPU 每次总是从内存中取出 4 个字节, 从内存编号为 0 的地方开始, 现在我定义一个 char a, 定义一个 int b, 让他们按顺排列在内存中, 就是这样的:
char a 占用 1 个字节, int a 占用 4 个字节, CPU 每次总是取 4 个字节, 这时我想要取 b 时, 需要先取出 0-3, 再取出 4-7, 然后将 1-4 拼在一起, 这样就需要取两次, 但是, 如果我让 char a 和 int b 按照特定的顺序排列:
这样我只需要取一次就能将 b 取出, 提升了 CPU 的工作效率.
字节对齐的概念和规则
概念:
数据类型自身的对齐值: 对于 char 型数据, 其自身对齐值为 1, 对于 short 型为 2, 对于 int,float 类型, 其自身对齐值为 4, 对于 double 型, 其自身对齐值为 8(单位字节).
结构体或者类的自身对齐值: 其成员中自身对齐值最大的那个值.
指定对齐值:
#pragma pack (value)
时的指定对齐值 value.
数据成员, 结构体和类的有效对齐值: 自身对齐值和指定对齐值中小的那个值.
规则:
有效对齐值 N 是最终用来决定数据存放地址方式的值, 最重要.
有效对齐 N, 就是表示 "对齐在 N 上", 也就是说该数据的 "存放起始地址 %N=0". 而数据结构中的数据变量都是按定义的先后顺序来排放的. 第一个数据变量的起始地址就是数据结构的起始地址. 结构体的成员变量要对齐排放, 结构体本身也要根据自身的有效对齐值圆整.
举例说明
代码如下
- #include <stdio.h>
- struct Test
- {
- char a;
- int b;
- short c;
- };
- int main(void)
- {
- struct Test t = {'a',1,2};
- printf("%d \n",sizeof(t));
- getchar();
- return 0;
- }
如果我们不知道字节对齐规则, 那么一定会认为这个结构体的大小是这样的, char 类型 1 个字节, int 类型 4 个字节, short 类型 2 个字节一共 7 个字节, 执行一下看看结果:
执行之后的结果是 12, 我们先来看一下反汇编代码:
根据反汇编代码我们可以看到 a,b,c 中的值分别存放在 ebp-0ch,ebp-8,ebp-4 这三个地方, 我们到内存中看一下他们是怎么排列的:
可以看到 char 占用 1 个字节, int 占用 4 个字节, short 占用 2 个字节, 但是并没有我们想象的那样紧挨着排放, 而是有一定的排放规则. 这里就体现出了字节对齐, 因为我这里是 32 位的机器, 默认是 4 字节对齐, 下面来详细的说一下是怎么排列的:
假设基址为 0012FF3C, 从偏移地址为 0 的位置开始存放
根据上面的字节对齐规则, a 是 char 类型, 自身对齐值就是 1 字节, b 是 int 类型, 自身对齐值是 4 字节, c 是 short 类型, 自身对齐值是 2 字节
结构体的自身对齐值: 结构体中自身对齐值最大的那个, 根据上面的分析这个结构体的自身对齐值为 4 字节
指定对齐值: 因为我们没有指定对齐值, 使用的是默认的 4 字节对齐
数据成员有效对齐值: 自身对齐值和指定对齐值中小的那个值, a 的自身对齐值是 1, 指定对齐值是 4, 较小值是 1, 那么 a 的对齐值就是 1;b 的自身对齐值是 4, 指定对齐值是 4, 较小值是 4, 那么 a 的对齐值就是 4;c 的自身对齐值是 12, 指定对齐值是 4, 较小值是 2, 那么 c 的对齐值就是 2, 假设有一个变量是 long 类型, 那么这个变量的对齐值应该为 4.
这样, a 占用 1 个字节, 存放在偏移地址为 0 的内存, 0%1=0, 没有问题.
b 占用 4 个字节, 如果将他挨着变量 a 存放, 也就是存放在偏移地址为 1 的位置, 1%4=1, 这样就存在问题了, 所以 b 存放到偏移地址为 4 的位置, 4%4=0.
最后, c 占用 2 个字节, 如果将他挨着 b 存放, 也就是存放到偏移地址为 8 的位置, 8%2=0, 没有问题, 所以 c 存放到 偏移地址为 8 的位置, 占两个字节.
这样就排列好了, 这时, 发现 a,b,c 一共才占用了 10 个字节, 因为结构体还没有根据自身有效对齐值圆整, 根据上面的分析, 这个结构体的自身对齐值是 4,12%4=0, 所以结构体会再占用两个字节, 10 和 11, 也就是 A 和 B, 这样就得出这个结构体的实际大小是 12.
当然, 除了使用默认的对齐值, 我们还可以自己使用 #pragma pack (value) 指定对齐值:
- #include <stdio.h>
- #pragma pack (2)
- struct Test
- {
- char a;
- int b;
- short c;
- };
- int main(void)
- {
- struct Test t = {'a',1,2};
- printf("%d \n",sizeof(t));
- getchar();
- return 0;
- }
这将对齐值设置成 2, 那么 a,b,c 在内存中的排列方式应该是这样的:
要注意, 这里的 int b 的自身对齐值是 4 字节, 有效对齐值是 2 字节.
来源: http://www.bubuko.com/infodetail-3402114.html