一、内存为什么要对齐

虽然所有的变量都是保存在特定地址的内存中,但最好还是按照内存对齐的要求来存储。这主要出于两个方面的原因考虑:

  1. 平台原因:
    不是所有的硬件平台(特别是嵌入式系统中使用的低端处理器)都能访问任意地址上的任意数据,某些硬件平台只能访问对齐的地址,否则会出现硬件异常。
  2. 性能原因:
    如果数据存放在未对齐的内存空间中,则处理器访问变量时需要进行两次内存访问才能完整读取该变量的值,而对齐的内存访问仅需一次访问。

二、内存对齐的规则

  1. 对于结构(或联合)的各个成员,第一个成员位于偏移为 0,以后每个数据成员的偏移量必须是#pragma pack指定的数值和结构体(或联合)中最大数据成员长度 这2个数值中较小的一个的倍数。
    使用伪代码表示: min(#pragma pack, 结构最大数据成员长度) * N

  2. 在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐也按照#pragma pack指定的数值和结构(或联合)最大数据成员长度这2个数值中较小的一个进行。

如果没有使用#pragma pack指令来显式的指定内存对齐的字节数,则按照默认字节数来对齐,各个平台的默认对齐规则如下:32位CPU默认按照4字节对齐;64位CPU默认按照8字节对齐。

三、#pragma pack 命令

1
2
3
#pragma pack(n) // 使用自定义n字节对齐  n可以为1,2,4,8,16
#pragma pack() // 使用缺省字节对齐
#pragma pack(show) // 在编译输出窗口以警告的形式显示出当前的内存以几个字节对齐

四、结构体内存对齐示例

在 64 位系统上编译下面的测试程序,已知在 64 位系统上各类型占用字节数如下:

1
2
3
4
5
6
char     1字节
short 2字节
int 4字节
long 4字节
double 8字节
long long 8字节

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma pack(8)

struct A {
char s[5];
short c;
int a;
};

int main()
{
int i = sizeof(A);
printf("%d", i);

return 0;
}

按照第二节所讲的内存对齐规则,分析如下:
因为结构体中最大的数据成员长度为 int(即 4 字节),而且#pragma pack(8)指令指定按照 8 字节对齐,所以min(4,8) = 4,故我们可以知道结构体 A 按照 4 字节对齐。

下图是结构体 A 按照 4 字节对齐的内存布局(需要注意的是“内存不是填充在 s5 后面,而是填充在 c 后面”):

从图我们很容易知道sizeof(A) = 12.

文章图片带有“CSDN”水印的说明:
由于该文章和图片最初发表在我的CSDN 博客中,因此图片被 CSDN 自动添加了水印。