关于sizeof与#pragma pack 以及网络上关于字节对齐的一点感想

工作中面试中对于字节对齐基本上是必考一个知识点,而很多面试是网络上上原题。基本上背一背就可以写正确,而关于4字节对齐我相信很多人也只是一个基本地了解,对于一些题目就感觉有问题,而且很多blog后面仍然有很多人在问一些题目,说明该blog并没有讲清楚这个问题。然后我自己也有疑问,所以就找了一些更多资料,终于是解了。

比如说

对于32位机器

1 #pragma pack(4) 
2 struct a{short a;short b;short c;} ; 3 sizeof(a)==6 //为什么不为8

不是说按4字节对齐那么最后面的一个short应该填充2字节的padding吗?该问题在百度知道上有人提问,问到最后我估计提问者也是花好久才可能明白,或者后面也没有明白

百度提问地址: http://zhidao.baidu.com/link?url=SVyAiSChbzcvTNIWuQP8RbfleHxTYJPgtSbGtpm5dOQM3M7FHZzqy0UVmqL5zNOuO2XeELgGbg7cafRuIs5_Za

引用CSDN上一个讲解字节对齐评论中的提问:

大神,我发现有点小问题,可能是我理解不当

1 struct A{
2     char a;
3     double b;
4     int c;
5     char d;
6 };//sizeof(A) = 24

 

 

如果按照规矩来这个明显要按照8字节对齐,

可为什么32位计算机还是要按照8字节对齐?显然我按照4字节对齐,总线访问效率更高呀?我觉得为什么字节对齐不受制于总线长度?望不吝赐教,小生在此拜谢

该Blog地址:http://blog.csdn.net/21aspnet/article/details/6729724

总体来讲要解决上述问题应该了解不仅仅是4字节对齐这么简单地去记忆,应该按照

对于结构体,在使用sizeof的时候会进行字节的对齐,对齐的规则如下: 
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 
   备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。 
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding); 
  备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。 
3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。 
  备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。

以上3条要认真学习就知道要对于结构体而讲要注意最宽的基本数据类型,还有每个成员对于首地址的offset是成员大小的整数倍就应该明白

struct
{
      char a;
      double b;      
}A;//sizeof(A)=16

而不是单单记在常考的32位机下4字节对齐这么简单。

当然最后也推荐一个自己认为讲得很清楚地BLOG,我怕内容有删除,就转载过来。地址为http://blog.csdn.net/nellson/article/details/5293588

 对于结构体,在使用sizeof的时候会进行字节的对齐,对齐的规则如下: 
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 
   备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。 
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding); 
  备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。 
3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。 
  备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。 
按照上面的规则,对如下几个例子进行分析: 
例1: 

1struct DATA1 
2
3    char    c1; //偏移量0,累积size = 1 
4    char    c2; //偏移量1,累积size = 1 + 1 = 2 
5    short    si; //偏移量2,累积size = 2 + 2 
6}; 

这个例子中,首先找到结构体变量的首地址,按照规则1,该首地址需要能被最宽基本类型成员整除,即能被short(字节数2)整除。接下来,为c1分配内存,由于其占一个字节,此时内存状态为*,然后,为c2分配内存,由于结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,而c2是char型成员,占用一个字节,刚好能够对齐,此时内存状态为**,最后,为short型成员分配内存,由于前两个成员占2个字节,si相对于结构体首地址的偏移量刚好是2的整数倍,所以,此时内存状态为**** 

例2: 
1struct DATA2 
2
3    char    c1; //偏移量0,累积size = 1 
4    short    si; //偏移量1 + (1),累积size = 1 + (1) + 2 = 4 
5    char    c2; //偏移量4,累积size = 4 + 1 = 5,但按最大长度sizeof(short) = 2对齐,故最后取6 
6}; 

这个例子中,首先找到结构体变量的首地址,与上例同。接下来,为c1分配内存,由于其占一个字节,此时内存状态为*,然后,为si分配内存,由于结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,而si是short型成员,占用2个字节,而前一个成员占用一个字节,所以需要在c1与si之间补一个字节进行对齐,此时内存状态为*×**,最后,为char型成员分配内存,由于前两个成员占4个字节,c2相对于结构体首地址的偏移量是1的整数倍,所以,此时内存状态为*×***,接下来,按照规则3,结构体的总大小为结构体最宽基本类型成员大小的整数倍,所以总大小应该是short(2字节)型变量大小的整数倍,需要再补一个字节,即*×***×,总共占6个字节 

例3: 
1struct DATA3 
2
3    char    c1; //偏移量0,累积size = 1 
4    double    d; //偏移量1 + (7),累积size = 1 + (7) + 8 = 16 
5    char    c2; //偏移量16,累积size = 16 + 1 = 17,但按最大长度sizeof(double) = 8对齐,故最后取24 
6}; 


关于#pragma pack(n) 
VC中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。 
例4: 
1#pragma pack(push) //保存对齐状态 
2#pragma pack(2)//设定为2字节对齐 
3struct DATA4 
4
5    char    c1; 
6    double  d1; 
7    int    i1; 
8    short  s1; 
9}; 
10#pragma pack(pop)//恢复对齐状态 
以上结构的大小为16,下面分析其存储情况,首先为c1分配空间,其偏移量为0,c1占用1个字节,由于1<2,所以按照char类型变量默认的对其方式。接着开始为d分配空间,这时其偏移量为1,需要补足1个字节,这样使偏移量满足为n=2的倍数(因为sizeof(double)大于n),d占用8个字节。接着为i1分配空间,这时其偏移量为10,满足为2的倍数,i1占用4个字节。这时已经为所有成员变量分配了空间,共分配了14个字节。 接下来再为s1分配空间,由于min(sizeof(s1),2)=2,所以s1按照2字节对齐,此时已经是对其的,所以分配2字节内存,这时,总共占用16字节 

例5: 
1#pragma pack(push) //保存对齐状态 
2#pragma pack(4)//设定为4字节对齐 
3struct DATA4 
4
5    char    c1; 
6    double  d; 
7    int    i1; 
8    short  s1; 
9}; 
10#pragma pack(pop)//恢复对齐状态 
以上结构的大小为20,下面分析其存储情况,首先为c1分配空间,其偏移量为0,c1占用1个字节,由于1<2,所以按照char类型变量默认的对其方式。接着开始为d分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),d占用8个字节。接着为i1分配空间,这时其偏移量为12,满足为4的倍数,i1占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节。接下来再为s1分配空间,由于min(sizeof(s1),4)=2,所以s1按照2字节对齐,此时已经是对齐的,所以分配2字节内存,这时,总共占用16字节,由于占用内存总数需要为4的整数倍,所以结构大小为20 

【参考文献】 
1.《对几组sizeof信息的分析》 http://blog.vckbase.com/billdavid/archive/2004/06/23/509.html 
2.《sizeof(结构体)和内存对齐http://www.ksarea.com/articles/20071004_sizeof-struct-memory.html

 

posted @ 2015-03-25 11:14  YorkYu  阅读(123)  评论(0编辑  收藏  举报