C/C++内存对齐详解

          C/C++内存对齐详解

 

        如果体系结构是不对齐的,A中的成员将会一个挨一个存储,从而可能出现sizeof(A)为11的情况。如果按照4字节倍数对齐,这时候sizeof(A)结果是12,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡,对齐可以节省CPU存取数据的时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的,它的设计也是从优先提高对w位数据操作的效率来考虑的。 

        下面给出三条判断内存对齐的规则,在没有#pragma pack宏的情况下,结构体sizeof的大小就是根据以下三条规则得出的:

        1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,个数据以后每成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

        2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储 (struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储)。

        3、收尾工作:结构体的总大小,也就是sizeof的结果, 必须是其内部最大成员的整数倍,不足的要补齐,但是如果内部最大成员的大小大于编译器默认的对齐大小,那么结构体的总大小必须是编译器默认大小的整数倍,即base=min(max(sizeof(S.a), sizeof(S.b),...sizeof(S.n)), default),其中default=#pragma pack(n) 

 

下面看一个例子:

typedef struct bb
{
     int id;                     //[0]....[3]
     double weight;      //[8].....[15]                                                         原则1
     float height;          //[16]..[19],总长要为8的整数倍,补齐[20]...[23]     原则3
} BB;

 

typedef struct aa
{
     char name[2];      //[0],[1]
     int  id;                  //[4]...[7]          原则1

     double score;     //[8]....[15]    
     short grade;       //[16],[17]        
     BB b;                 //[24]......[47]          原则2
} AA;

int main()
{
    AA a;
   cout<<sizeof(a)<<" "<<sizeof(BB)<<endl;
   return 0;
}

 

输出结果是:

48 24

 

        上面是在VC环境根据默认对齐方式得到的结果,VC,VS等编译器默认是#pragma pack(8),同时支持1,2,4,8,16对齐,但是注意gcc默认的是#pragma pack(4),并且gcc只支持1,2,4对齐。

 

看下面一个例子 :

struct S1
{
        short a;  // 2 byte
        long b;   // 4 byte
};

struct S2
{
        char c;           //1byte
        struct S1 d;      //8bytes
        long long int e;  //8 bytes

};

 

        在gcc下, sizeof(struct S2) = 20, 但在VC 6.0下却是24! 为什么会是这样的呢?这就涉及到编译器的默认对齐和三原则中的原则三的问题了。对于上面结构体S2,如果不考虑内存对齐,那么sizeof(S2)的大小是17bytes,如果要进行对齐,这时候需要满足规则三,在gcc下,编译器默认对齐的大小是4bytes,而S2最大元素大小是8bytes,所以在GCC下按照4bytes对齐,最终得到的S2的大小就是20bytes;但是在VC6.0下,编译器默认对齐是8bytes,因此按照8bytes对齐,因而最终得到的S2的大小是24bytes。但是我们可以修改编译器默认的对齐大小,可以在结构体前后分别加上下面宏即可:

#pragma pack(n)
...
#pragma pack()

 

        经过测试, 在gcc和VC 6.0中,当分别指定n值为1,2,4时,struct S2的大小相同,分别为15, 16, 20。n=1时, 空间最省,但存取效率低了。但n=8时,gcc 计算的结果为20, VC 6.0计算的结果为24. 貌似使用#pragma pack(8)对gcc不起作用,还是按默认的4字节对齐。为什么对于#pragma pack(8)这种情况,gcc不起作用呢?这个又回到了上面所提到的问题,即在gcc下只支持1,2,4对齐。那是否我们就无能为力了呢?非也,如果非要指定gcc按8字节对齐也是有办法的,可以使用__attribute__ ((aligned(8))),具体用法如下所示:

struct S1
{
        short a;
        long b;
};

struct S2
{
        char c;
        struct S1 d;
        long long int e;
} __attribute__ ((aligned(8)));

 

这样,再计算sizeof(struct S2)时,在GCC下得到的结果也是24了。

 

参考

http://blog.csdn.net/zhangyang0402/article/details/5799977

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值