如何理解 struct 的内存对齐?

[图片]
关注者
527
被浏览
386,008

32 个回答

字节对齐主要是为了提高内存的访问效率,比如intel 32位cpu,每个总线周期都是从偶地址开始读取32位的内存数据,如果数据存放地址不是从偶数开始,则可能出现需要两个总线周期才能读取到想要的数据,因此需要在内存中存放数据时进行对齐。


通常我们说字节对齐很多时候都是说struct结构体的内存对齐,比如下面的结构体:

struct A{
    char a;
    int b;
    short c;
}

在32位机器上char 占1个字节,int 占4个字节,short占2个字节,一共占用7个字节.但是实际真的是这样吗?

我们先看下面程序的输出:

#include <stdio.h>

struct A{
    char a;
    int b;
    short c;
};
int main(){
    struct A a;
    printf("A: %ld\n", sizeof(a));
    return 0;
}

测试输出的结果是A: 12, 比计算的7多了5个字节。这个就是因为编译器在编译的时候进行了内存对齐导致的。


内存对齐主要遵循下面三个原则:

  1. 结构体变量的起始地址能够被其最宽的成员大小整除
  2. 结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节
  3. 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节

其实这里有点不严谨,编译器在编译的时候是可以指定对齐大小的,实际使用的有效对齐其实是取指定大小和自身大小的最小值,一般默认的对齐大小是4。


再回到上面的例子,如果默认的对齐大小是4,结构体a的起始地址为0x0000,能够被最宽的数据成员大小(这里是int, 大小为4,有效对齐大小也是4)整除,姑char a的从0x0000开始存放占用一个字节即0x0000~0x0001,然后是int b,其大小为4,故要满足2,需要从0x0004开始,所以在char a后填充三个字节,因此a对齐后占用的空间是0x0000~0x0003,b占用的空间是0x0004~0x0007, 然后是short c其大小是2,故从0x0008开始占用两个字节,即0x0008~0x0009。 此时整个结构体占用的空间是0x0000~0x0009, 占用10个字节,10%4 != 0, 不满足第三个原则,所以需要在后面补充两个字节,即最后内存对齐后占用的空间是0x0000~0x000B,一共12个字节。

5分钟教你秒杀:

前言:(10秒)

前面的各位的方法很好,很传神,不适合速成,想要速成还得看我这种小屁孩写的

公式:(20秒)

公式1:前面的地址必须是后面的地址正数倍,不是就补齐

公式2:整个Struct的地址必须是最大字节的整数倍

30秒....敌人还有三十秒到达战场...来看看下面的Ex(不是前任...!)

Struct E1

{ int a;char b; char c}e1;

第一地址肯定存放a是4Byte地址,第二地址,b要1Byte的地址,来欢迎公式一登场: 4 == 1*N (N等于正整数) 答""!地址现在为5Byte,下一个c要1Byte的地址同上,所以,就是6Byte。来欢迎公式二登场,在这个E1中最大的字节是4,而我们的地址字节是6,4的整数倍不是6,所以,要加2Byte(总地址),So,整个字节为8!

CAUTION:

每个特定平台上的编译器都有自己的默认“对齐系数”。可以通过预编译命令#pragma pack(n)

喝下茶,缓缓