QQ_Sprite

导航

有关内存字节对齐问题的总结

    以前也折腾过这个问题,不过不如这次来的更深刻了。

    最早的资料来源是陈正冲·石虎的《C语言深度剖析》,但是看过之后还是有些模糊。xczhang的文章用了具体的例子来分析:

    http://www.cppblog.com/xczhang/archive/2007/12/23/39393.html

    他的原话是:

    “事实上,很多人对#pragma pack的理解是错误的。#pragma pack规定的对齐长度,实际使用的规则是:
    结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
    也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
    而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。”

    这里提到了3个对比的优先级:#pragma pack(n)的参数n、结构体最大成员的长度、每个成员在“摆放”位置时自己的自身的长度。

    还是用一个例子来说明吧:

struct TestStruct
{
    char a;
    long long b;
    short c;
    //int d;
    //char d;
    short d;
     //用一个默认构造函数进行初始化,便于在“内存”面板查看内存块使用情况    TestStruct::TestStruct() : a('a'), b(10L), c(2), d(3){}
};

int normal_size = sizeof(TestStruct);
printf("sizeof(TestStruct4) result is : %d\n", normal_size);

 

成员a、b、c在内存中的排列情况是:

61 cc  cc cc  cc  cc cc  cc   //成员a(char):a???????

0a 00 00 00 00 00 00 00  //成员b(long long):10

02 00                             //成员c(short):2

 

解释:整个结构体最大成员为b,long long长度为8。首先放成员a:a为char型长度为1,放入[0]号位置。

         成员b长度为8,其首元素位置下标必须为8的倍数,取最小值,故将b从[8]开始摆放。

         成员c长度为2,首元素位置下标必须为2的倍数,此时最小下标为[16],放入。

注意TestStruct最后一个成员d,当d的类型为char时,第三行排布情况为(假设d初始化为字符'd'):

02 00 64 cc cc cc cc cc

解释:d的自身长度为1,其下标必须为1的倍数,故将其放在c的后面(下标[18]),中间不留空位,剩余部分由“空位(cc)”填充。

 

当d类型为short时,第三行排布情况为(假设d值为3):

02 00 03 00 cc cc cc cc

解释:d自身长度为2,满足2的倍数条件的最小下标为[18]

 

当d类型为int时,第三行排布情况为(假设d值为3):

02 00 cc cc 03 00 00 00

解释:d自身长度为4,满足2的倍数条件的最小下标为[20]。故c、d之间补两个空位(cc)

不加pragma pack(n),三种情况结构体大小都一样,为24。

如果加了呢?这里以 pragma pack(2),即我们以2作为对齐长度

解释:整个结构体最大成员为b,long long长度为8。首先放成员a:a为char型长度为1,放入[0]号位置。

成员b长度为8,其首元素位置下标必须为8的倍数,取最小值,故将b从[8]开始摆放。

成员c长度为2,首元素位置下标必须为2的倍数,此时最小下标为[16],放入。

注意TestStruct最后一个成员d,当d的类型为char时,第三行排布情况为(假设d初始化为字符'd'):

02 00 64 cc cc cc cc cc

解释:d的自身长度为1,其下标必须为1的倍数,故将其放在c的后面(下标[18]),中间不留空位,剩余部分由“空位(cc)”填充。

 

当d类型为short时,第三行排布情况为(假设d值为3):

02 00 03 00 cc cc cc cc

解释:d自身长度为2,满足2的倍数条件的最小下标为[18]

 

当d类型为int时,第三行排布情况为(假设d值为3):

02 00 cc cc 03 00 00 00

解释:d自身长度为4,满足2的倍数条件的最小下标为[20]。故c、d之间补两个空位(cc)

不加pragma pack(n),三种情况结构体大小都一样,为24。

如果加了呢?这里以 pragma pack(2),即我们以2作为对齐长:

#pragma pack(2)
struct TestStruct
{
    char a;
    long long b;
    short c;
    //int d;
    //char d;
    short d;
  //用一个默认构造函数进行初始化,便于在“内存”面板查看内存块使用情况
TestStruct::TestStruct() : a('a'), b(10L), c(2), d(3){}
}; #pragma pack() int normal_size = sizeof(TestStruct); printf("sizeof(TestStruct4) result is : %d\n", normal_size);

 

成员a、b、c在内存中的排列情况是:

61 cc     //成员a(char):a?

0a 00

00 00

00 00

00 00  //成员b(long long):10

02 00  //成员c(short):2

 

解释:整个结构体最大成员为b,long long长度为8。由于加了#pragma pack(2),2比8要小,整个结构体以2作为对齐长度。

        首先放成员a:a为char型长度为1,放入[0]号位置。

         成员b长度为8,其首元素位置下标必须为2的倍数,取最小值,故将b从[2]开始摆放。

         成员c长度为2,首元素位置下标必须为2的倍数,此时最小下标为[10],放入。

注意TestStruct最后一个成员d,当d的类型为char时,第三行排布情况为(假设d初始化为字符'd'):

02 00

64 cc

解释:d的自身长度为1,其下标必须为1的倍数,故将其放在c的后面(下标[12]),中间不留空位,最后一位由“空位(cc)”填充。

此时结构体大小为14 = 2+8+2+2

 

当d类型为short时,第三行排布情况为(假设d值为3):

02 00

03 00

解释:d自身长度为2,满足2的倍数条件的最小下标为[12],此时结构体大小为14 = 2+8+2+2

 

当d类型为int时,第三行排布情况为(假设d值为3):

02 00

03 00

00 00

解释:d自身长度为4,满足2的倍数条件的最小下标为[12]。int内存块视作拆成两个short

此时结构体大小为:16 = 2+8+2+4

 

#pragma pack(4)和#pragma pack(8)同理,这里不做赘述

 

posted on 2013-04-22 18:58  QQ_Sprite  阅读(184)  评论(0编辑  收藏  举报