日期:2014-05-16  浏览次数:20875 次

linux 内存对齐问题分析
每个特定平台的编译器都有一个默认的对齐系数,gcc中是4,VC中貌似是8。也可以通过于编译命令#pragma pack(n)来指定该系数,其中n的值经测试只能是1,2和4.

对齐规则:

1、结构体的第一个数据成员放在相对位置为0的地方,以后每个数据成员按#pragma pack(n)中n指定的值和该数据成员自身长度中比较小的那个进行对齐。

2、数据成员完成对齐后,结构体本身也要对齐,按照#pragma pack(n)中n的值和结构体数据成员中最长的长度中较小的进行对齐。

验证(环境:gcc 4.4;sizeof(char)=1;sizeof(int)=4;sizeof(short)=2;sizeof(long)=4;sizeof(long long)=8):

1、默认情况(n=4)

struct st1 {

char ch;//长度1<n,按1对齐,0%1=0,起始相对位置=0;存放区间[0]

int num;//长度4=n,按4对齐, 4%4=0,起始相对位置=4;存放区间[4,7]

long lv;//长度4=n,按4对齐,8%4=0,起始相对位置=8;存放区间[8,11]

};

整个结构体成员对齐后所占的区间为[0,8],占12个字节,接着结构体本身对齐,成员中最长的是4,n也等于4,所以结构体本身按4对齐(即对齐系数)。

整个结构体的大小 = 比整个结构体数据成员所占的总空间大或相等且和对齐系数求模结果为0、与之距离最近的数。

本例中,12%4=0,所以结构体st1占12个字节的空间。

2、#pragma pack(1)(即n=1)

struct st1 {

char ch;//长度1=n,按1对齐,0%1=0,起始相对位置=0;存放区间[0]

int num;//长度4>n,按n对齐, 1%1=0,起始相对位置=0;存放区间[1,4]

long lv;//长度4>n,按n对齐,5%1=0,起始相对位置=5;存放区间[5,8]

};

整个结构体成员对齐后所占的区间为[0,8],占9个字节,接着结构体本身对齐,成员中最长的是4,n等于1,所以结构体本身按1对齐(即对齐系数)。

整个结构体的大小 = 比整个结构体数据成员所占的总空间大或相等且和对齐系数求模结果为0、与之距离最近的数。

本例中,9%1=0,所以结构体st1占9个字节的空间。

3、#pragma pack(2)(即n=2)

struct st1 {

char ch;//长度1<n,按1对齐,0%1=0,起始相对位置=0;存放区间[0]

int num;//长度4>n,按n对齐, 2%2=0,起始相对位置=2;存放区间[2,5]

long lv;//长度4>n,按n对齐,6%2=0,起始相对位置=6;存放区间[5,8]

};

整个结构体成员对齐后所占的区间为[0,8],占9个字节,接着结构体本身对齐,成员中最长的是4,n等于2,所以结构体本身按2对齐(即对齐系数)。

整个结构体的大小 = 比整个结构体数据成员所占的总空间大或相等且和对齐系数求模结果为0、与之距离最近的数。

本例中,10%2=0,所以结构体st1占10个字节的空间。

为什么说#pragma pack(n)中n只能是1,2,4呢?

比如3,如果n=3,在编译的时候会警告“对齐边界必须是 2 的较小次方,而不是 3”,也就是说是不起作用的,按默认对齐系数对齐。

再如8,会有什么结果?看下一例:

4、#pragma pack(8)(即n=8)

struct siz {

char v1;

long long v2;

short v3;

int v4;

};

如果8起作用,分析一下:

struct siz {

char v1;//长度1<n,按1对齐,0%1=0,起始相对位置=0;存放区间[0]

long long v2;//长度8=n,按8对齐,8%8=0,起始相对位置=8;存放区间[8,15]

short v3;//长度2<n,按2对齐,16%2=0,起始相对位置=16;存放区间[16,17]

int v4;//长度4<n,按4对齐,20%4=0,起始相对位置=20;存放区间[20,23]

};

整个结构体成员对齐后所占的区间为[0,23],占24个字节,接着结构体本身对齐,成员中最长的是8,n等于8,所以结构体本身按8对齐(即对齐系数)。24%8=0,所以占24个字节。

然而,很不幸,运行的结果是20.

接下来,用默认的对齐系数4来分析一下:

struct siz {

char v1;//长度1<4,按1对齐,0%1=0,起始相对位置=0;存放区间[0]

long long v2;//长度8>4,按4对齐,4%4=0,起始相对位置=4;存放区间[4,11]

short v3;//长度2<4,按2对齐,12%2=0,起始相对位置=12;存放区间[12,13]

int v4;//长度4=4,按4对齐,16%4=0,起始相对位置=16;存放区间[16,19]

};

整个结构体成员对齐后所占的区间为[0,19],占20个字节,接着结构体本身对齐,成员中最长的是8,n等于4,所以结构体本身按4对齐(即对齐系数)。20%4=0,所以占20个字节。与运行结果一致。

综上分析,当n=8的时候gcc仍然使用的是默认的对齐系数4.