关于C/C++中的数据对齐的一些疑问

初次接触“数据对齐”这个概念的时候,大概是在学习C语言基础结构体的时候,当时就记得老师说,这是以后找工作面试最常问的一个问题。后来虽然把基本思路摸清楚了,但是关于在计算机系统中的具体原因,我也没有去仔细深究,大概是为以空间去换取效率吧。昨天晚上浏览博客园的时候,注意到有个朋友写了这样一篇,看的时候总觉得解释的比较别扭,和我之前的理解大相径庭。由于是手机上网,也没有多加验证,今天打开电脑突然想起这个问题,就简单编写测试程序验证了一下。也把数据对齐这个概念重新温习了一遍。

找了许多中文资料,关于数据对齐的原因解释是这样的:

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬
件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问
未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

先看第一个例子:

  1. #include <iostream>   
  2. using namespace std;       
  3. struct s1{    
  4.         char k;   
  5.         double i;   
  6.         char c;   
  7. };   
  8. int main(int argc,char **argv)   
  9. {   
  10.         cout << sizeof(struct s1) << endl;   
  11.         return 0;   
  12. }  

这个例子摘自于“博客园”,上面的解释的输出结果是24,而我在Redhat X86 用GCC编译输出结果却是16,符合之前我记得的两条数据对齐的规则:1.默认按照4字节补齐;2.如果所有成员都小于默认的补齐方式,则按照所有成员最大的成员进行补齐。但是仔细一看博客园的解释,觉得也对,上面解释大概是是这样的:

如果是连续的存每个字段的值,那么double类型的i就可能分配在两个8字节的存储器块中,那么可能就要执行两次存储器访问,而现在的内存分配至这样的,k,i,c都占用8个字节,这样访问1次存储器就能得到double类型的i的值。k后面会有7个字节被浪费掉,也就是说i的偏移量是8,同样的c后面有7个字节被浪费掉,尽管它后面没有任何字段。这样显然太浪费内存了。

我能想到的解释是:不同机器中的对齐方式不一样,关于这点,我也整理了一些资料查看,在文章末尾提供参考。。

再来看另一个程序如下:

  1. #include <iostream>   
  2. using namespace std;   
  3. typedef struct{   
  4.         char c1;    
  5.         long l;   
  6.         char c2;    
  7.         double d;   
  8. }a;   
  9.   
  10. typedef struct{   
  11.         char c1;    
  12.         char c2;    
  13.         long l;   
  14.         double d;   
  15. }b;   
  16. int main(int argc,char **argv)   
  17. {   
  18.         cout << "sizeof(a) = " << sizeof(a) << endl;   
  19.         cout << "sizeof(b) = " << sizeof(b) << endl;   
  20.         return 0;   
  21. }  

这个程序作者应该是windows xp下运行的并且采用vc++6.0进行编译执行的。上面这个程序的执行结果是

但是同样的程序在linux下gcc编译执行结果是

关于上面两个程序分析如下:

1.在windows xp下采用vc++6.0编译时,c1为char类型占用一个字节,之后为了让l自然对齐,因此需要填充3个字节。l为long型,占用4个字节,c2为char型,为了让双精度的d对齐,需要在c2添加7个字节,之后d占用8个字节。所以结果为:1+3+4+1+7+8=24。按同样的规则易得结构体b所占空间为16。

2.在linux下采用gcc编译时,补齐按照4个字节来进行补齐,也就是说结构体a中四个元素分别占用空间大小为4,4,4,8,总共为20,其中20为4个倍数,也满足linux下数据对齐的原则。

看来造成这样的诧异,应该是与平台以及编译器有关系。

以上的对齐方式,称作自然对齐,在学习C编译工具的时候,记得可以通过设置pragma来强制对齐,比如上面的程序修改成如下:

  1. #include <iostream>   
  2. using namespace std;   
  3. #pragma pack(2)   
  4. typedef struct{   
  5.         char c1;    
  6.         long l;   
  7.         char c2;    
  8.         double d;   
  9. }a;   
  10.   
  11. typedef struct{   
  12.         char c1;    
  13.         char c2;    
  14.         long l;   
  15.         double d;   
  16. }b;   
  17. #pragma pack()   
  18. int main(int argc,char **argv)   
  19. {   
  20.         cout << "sizeof(a) = " << sizeof(a) << endl;   
  21.         cout << "sizeof(b) = " << sizeof(b) << endl;   
  22.         return 0;   
  23. }  

运行结果为:

分析:由于通过#pragma将对齐设置为2个字节,所以,结构体a中的四个元素占用空间大小为:2,4,2,8,总共为16.结构体b也类似。

总结一下,关于#pragma pack(n)强制对齐有以下两种情况:

  • 第一、如果n大于等于该成员所占用的字节数,那么偏移量必须满足默认的对齐方式,即自然对齐方式。
  • 第二、如果n小于该成员的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数; 否则必然为n的倍数。

关于数据对齐的一些疑问也就总结到这里,原文地址是:http://www.muxiaofei.com/1238.html,欢迎大家到这里提出意见,本文参考了许多博客园和CSDN  百度博客以及一些培训机构的资料,总结的不是很完善,主要是阐述了个人的一些理解,关于数据对齐有很多情况,这里就不一一列举,下面列出参考的网址以及一些PDF文档提供下载:

posted @ 2012-10-05 00:15  mengzhiyi  阅读(267)  评论(1编辑  收藏  举报