C笔记-数据结构对齐

作者:聂勇 欢迎转载,请保留作者信息并说明文章来源!

数据结构对齐其实涉及到两个概念:数据对齐(data alignment)和数据结构填充(data structure padding)。计算机以块为单位(在32位系统中,以4 bytes为单位)对内存进行数据读写,因为这样可以提升CPU处理的性能。另外,为了数据对齐,需要在部分数据的尾部插入一些无意义的数据进行填充。

数据对齐规则 | data alignment rules

每个平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这个系数。
1、 结构中的第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(对齐系数,数据成员的自身长度) 的倍数。
2、数据成员完成各自对齐之后,结构本身也要进行对齐。

数据对齐长度 | data alignment length

Microsoft (Visual C++), Borland/CodeGear (C++Builder), Digital Mars (DMC) and GNU (GCC)在32位x86平台中默认的对齐长度如下:

数据类型 对齐长度 备注
char 1 byte
short 2 bytes
int 4 bytes
long 4 bytes
float 4 bytes
double Windows:8 bytes
Linux:4 bytes (编译时增加可选参数 -malign-double 为8 bytes对齐)
long double Visual C++:8 bytes (待验证)
C++Builder / DMC:10 bytes (待验证)
GCC:12 bytes(待验证)
pointer 4 bytes  

在64位x86平台与32位x86平台有以下类型的默认的对齐长度不同:

数据类型 对齐长度 备注
long 8 bytes
double 8 bytes
long double Visual C++:8 bytes (待验证)
GCC:16 bytes
pointer 8 bytes  

实例 | example

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdlib.h>
#include <stdio.h>
int main ( int argc, char *argv[] )
{
struct data1{
char a;
char b;
int c;
char *d;
char e;
} a1;
struct data2 {
char a;
int c;
char b;
char *d;
char e;
} a2;
struct data3 {
char a;
char b;
char e;
int c;
char *d;
} a3;
struct data4 {
char a;
char *d;
char b;
char e;
int c;
} a4;
printf("sizeof(a1)=%lu\n", sizeof(a1));
printf("%p %p %p %p %p\n", &(a1.a), &(a1.b), &(a1.c), &(a1.d), &(a1.e));
printf("%ld %ld %ld %ld\n\n", (char*)&(a1.b) - (char*)&(a1.a), (char*)&(a1.c) - (char*)&(a1.b), (char*)&(a1.d) - (char*)&(a1.c), (char*)&(a1.e) - (char*)&(a1.d));
printf("sizeof(a2)=%lu\n", sizeof(a2));
printf("%p %p %p %p %p\n", &(a2.a), &(a2.c), &(a2.b), &(a2.d), &(a2.e));
printf("%ld %ld %ld %ld\n\n", (char*)&(a2.c) - (char*)&(a2.a), (char*)&(a2.b) - (char*)&(a2.c), (char*)&(a2.d) - (char*)&(a2.b), (char*)&(a2.e) - (char*)&(a2.d));
printf("sizeof(a3)=%lu\n", sizeof(a3));
printf("%p %p %p %p %p\n", &(a3.a), &(a3.b), &(a3.e), &(a3.c), &(a3.d));
printf("%ld %ld %ld %ld\n\n", (char*)&(a3.b) - (char*)&(a3.a), (char*)&(a3.e) - (char*)&(a3.b), (char*)&(a3.c) - (char*)&(a3.e), (char*)&(a3.d) - (char*)&(a3.c));
printf("sizeof(a4)=%lu\n", sizeof(a4));
printf("%p %p %p %p %p\n", &(a4.a), &(a4.d), &(a4.b), &(a4.e), &(a4.c));
printf("%ld %ld %ld %ld\n", (char*)&(a4.d) - (char*)&(a4.a), (char*)&(a4.b) - (char*)&(a4.d), (char*)&(a4.e) - (char*)&(a4.b), (char*)&(a4.c) - (char*)&(a4.e));
return EXIT_SUCCESS;
}

运行结果

sizeof(a1)=24
0x7fffbd59e0c0 0x7fffbd59e0c1 0x7fffbd59e0c4 0x7fffbd59e0c8 0x7fffbd59e0d0
1 3 4 8

sizeof(a2)=32
0x7fffbd59e0a0 0x7fffbd59e0a4 0x7fffbd59e0a8 0x7fffbd59e0b0 0x7fffbd59e0b8
4 4 8 8

sizeof(a3)=16
0x7fffbd59e090 0x7fffbd59e091 0x7fffbd59e092 0x7fffbd59e094 0x7fffbd59e098
1 1 2 4

sizeof(a4)=24
0x7fffbd59e070 0x7fffbd59e078 0x7fffbd59e080 0x7fffbd59e081 0x7fffbd59e084
8 8 1 3

内存示意图

内存示意图

说明:

  • 斜线部分表示填充区域
  • 上述代码在RedHat 5.7 b4bit系统中编译和运行,其默认的对齐系数是8 bytes

提示:

  • 当struct本身是一个struct或union中的成员时,其对齐系数是struct中最大长度的成员的长度。
  • 为了节省内存,占用内存小的成员放在结构的前面,占用内存大的成员放在结构的后面。