这个话题还是很早以前讨论过,当时并没有好好的理解,最近在复习知识的时候又重新看了一遍资料,自己做一下总结,也希望后面有人需要学习时可以对他有所帮助。以下我会举两个结构体的例子,分别画图的方式表达对齐的原则。

结构体对齐的公式

记住以下这些规则,把结构体往里面套就可以了。结构体对齐的原则就是牺牲空间的方式来减少时间的消耗,空间用完还可以复用,而时间过去了就再也不会回来了。

  • 以 #pragma pack(x) 中 x 的大小和结构中占用空间最大的成员做比较,取小值为 n(外对齐依据)
  • 以 n 值和结构体每个成员比较,得出结果列表为 m[x]
  • 根据每个成员的大小依次向内存中填充数据,要求填充 成员的起始地址 减去 构体起始地址 的差都可以整除 m[x] ,如不能整除则向后移动,直到可以整除再填充成员到内存(内对齐依据)
  • 当全部成员填充完毕后所占用的字节若不能整除 n,则扩充内存到可以整除 n 为止。

案例一

我们来看一个简单的案例,#pragma pack(4) 为 4,结构体中有 char、short、int 3个成员,其对齐的方式如下图表示: 2016-02-15_190135

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

#pragma pack(4)

typedef struct data
{
char a;
short b;
int c;
} DATA, *PDATA;

int main(int argc, char* argv[])
{
printf("sizeof(DATA) = %lu\n", sizeof(DATA));
return 0;
}

以上代码运行后,得到如下结果:

1
2
3
mycode@vmware:~/Desktop$ gcc struct.c -o struct_size
mycode@vmware:~/Desktop$ ./struct
sizeof(DATA) = 8

案例二

这个案例中,我们把 #pragma pack(8) 设定为 8,结构体中有三个成员 char、double、int,其对齐方式如下图: 2016-02-15_190143

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

#pragma pack(8)

typedef struct data
{
char a;
double b;
int c;
} DATA, *PDATA;

int main(int argc, char* argv[])
{
printf("sizeof(DATA) = %lu\n", sizeof(DATA));
return 0;
}

是不是有点毁三观啊?以上代码运行后的结果,的确就是 24。

1
2
3
mycode@vmware:~/Desktop$ gcc struct_size.c -o struct_size
mycode@vmware:~/Desktop$ ./struct_size
sizeof(DATA) = 24

要注意的是,如果你把这个案例中 int 和 double 成员颠倒个位置,再编译代码你会发现其占用空间变成了 16,按上面的规则推断一下,很明显,首先是第二个成员 int 在内存的第 4 号位置就可以驻留了,第三个成员 double,在第 8 号位置也同样可以驻留。最终大小会变成 16 个字节。