如何对齐?
正是由于内存对齐对于系统的运行至关重要,因此这部分往往是编译器根据目标机器的架构自动完成而无须程序员手动干预。但是,我们必须要了解数据是怎么对齐在内存中的,否则在通过地址获取数据值的时候就会出现问题,即你以为数据是在这个地址,实际它被对齐到了别处,导致你获取的数据是无效的。
一般来说在C语言中结构体的内存对齐有以下规则:
结构体中每个成员通常按照其类型的自然对齐界限对齐,自然对齐界限一般是该数据类型大小的整数倍,如 int 的大小为 4 字节,那么其自然对齐界限就是 4 字节,所以其地址应该是 4 的倍数。
结构体的总大小会被对齐到最大成员的对齐界限。如一个含有 int 和 char 类型成员的结构体,其对齐界限一般是 int 的对齐界限也就是最终结构体的总大小为 4 的倍数。
如果手动指定了对齐值,那么结构体成员的对齐值为 min(成员自身对齐值,指定对齐值),即取自身对齐值和指定对齐值中较小的值。而结构体的总大小为 min(所有成员中对齐值最大的,指定对齐值)的整数倍。
假设我们的硬件平台为 32 位,我们来看如下一段代码:
<p>#include <stdio.h></p><p>
</p><p>struct example1 {</p><p> char a; // 1 字节</p><p> int b; // 4 字节</p><p> short c; // 2 字节</p><p>};</p><p>
</p><p>int main(void) {</p><p> printf("sizeof(struct example1) = %lu\n", sizeof(struct example1));</p><p> return 0;</p><p>}</p>
代码中定义了一个结构体 example1,内部含有 1 个字节大小的 char 类型数据 a,4 个字节大小的 int 类型数据 b 以及 2 个字节大小的 short 类型 c。所有元素本身的大小总和为 7 个字节。下面我们运行一下这段代码看一看整个结构体的大小是多少:
<p>jay@jaylinuxlenovo:~/test/printest$ ./main </p><p>sizeof(struct example1) = 12</p>
可以看到,根据运行结果,整个结构体的大小是 12 个字节。自然是受了内存对齐机制的影响,我们结合上面的规则来分析一下:
首先 a 占了一个字节的大小,存在于结构体的起始地址。b 是一个 4 字节的整型数据,它的起始地址必须要是 4 的整倍数,因此在这里我们要在 a 的后面添加 3 个填充元素,然后再放 b。c 本身的大小为 2,由于 b 已经对齐到了 4 的边界,本身大小又是 4,此时 c 的地址为 8,恰好等于 2 的倍数,所以无须填充就是对齐的。到这里 3 个成员已经占用了 10 个字节的大小,而根据结构体自身的对齐原则,其总大小要是成员中最大类型的整倍数,在这里是 int 类型也就是需要是 4 的整倍数,因此在 c 后面还需要添加 2 个填充元素,以凑满 12 个字节,也就是 4 的倍数。最终这个结构体的完整内存分布如下( - 表示填充元素):
<p>+---+---+---+---+---+---+---+---+---+---+---+---+</p><p>| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| 11|</p><p>+---+---+---+---+---+---+---+---+---+---+---+---+</p><p>| a | - | - | - | b | b | b | b | c | c | - | - |</p><p>+---+---+---+---+---+---+---+---+---+---+---+---+</p>
|