转载_结构体中四字节对齐的详解

 

转载_结构体中四字节对齐的详解

  2967人阅读  评论(1)  收藏  举报
  分类:

一 四字节对齐的规则

C++中结构体变量的存储为什么有个4字节对齐的规则,这里是假设32位机器上,CPU在读取内存数据的时候4字节对齐会取得更快的速度;这是因为:1字节8位,4字节正好32位,而32位机器的寄存器,地址什么的都是32位的,正好一次处理就完成。


二 相关内容解释

例如,下面的结构各成员空间分配情况:

[cpp]  view plain copy
  1. struct test  
  2. {  
  3.      char x1;  
  4.      short x2;  
  5.      float x3;  
  6.      char x4;  
  7. };  
“结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。更改C编译器的缺省字节对齐方式。”

1、这个意思就是说前两个成员各占2字节,后两个成员各占4个字节,换句话说,每个成员占的字节数都和邻近它的前一个成员类型相关。
2、__attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。用这种方式做的缺点是什么呢?是不是因为以前计算资源有限,所以要通过这种字节对齐来节省资源,但现在硬件资源充足了,因此不用过多考虑这个问题?
3、#pragma pack(1) // 按照1字节方式进行对齐
[cpp]  view plain copy
  1. struct TCPHEADER  
  2. {  
  3.      short SrcPort; // 16位源端口号  
  4.      short DstPort; // 16位目的端口号  
  5.      int SerialNo; // 32位序列号  
  6.      int AckNo; // 32位确认号  
  7.      unsigned char HaderLen : 4; // 4位首部长度  
  8.      unsigned char Reserved1 : 4; // 保留6位中的4位  
  9.      unsigned char Reserved2 : 2; // 保留6位中的2位  
  10.      unsigned char URG : 1;  
  11.      unsigned char ACK : 1;  
  12.      unsigned char PSH : 1;  
  13.      unsigned char RST : 1;  
  14.      unsigned char SYN : 1;  
  15.      unsigned char FIN : 1;  
  16.      short WindowSize; // 16位窗口大小  
  17.      short TcpChkSum; // 16位TCP检验和  
  18.      short UrgentPointer; // 16位紧急指针  
  19. };  
[cpp]  view plain copy
  1. #pragma pack() // 取消1字节对齐方式  
这段code表明所有的成员都按照1字节对齐?那像short类型,只取第一个字节的值,后三个忽略?

4、每种数据类型是低位在前,还是高位在前呢?如何判断?根据OS或者其它的条件?

字节对齐在不同编译器下语法是不一样的,在GCC中是#pragma push(1)  #pragma pack(); 在MS C++中用VC的代码项里可以调整,默认是8字节;

[cpp]  view plain copy
  1. typedef struct   
  2. {  
  3.    char c;  
  4.    int  i;  
  5. }test;  
字节对齐,是对齐,比如说char 与 int 如果是4字节对齐,那么char也会占用4个字节,总共占8字节,而且结构体对象存储是按照顺序存的,char 肯定在int前面。第二种情况如果1字节对齐,意味着char只占1字节,而结下来int会占用4字节,这个N字节对齐的意思是,每个成员占用空间必须是N字节的倍数,不足N字节的占用N字节。那么以1字节对齐那它占用5个字节。
还有每种数据是低位还是高位在前,这个根处理器有关,Intel处理是小端对齐,比如说一个整数522387969用16进制表示是:0x1f 23 02 01,在Intel处理器中表示是0x01 02 23 1f,所以在内存用0x01 02 03 1f来示522387969,这就是所谓有小端对齐;但在arm处理器中522387969表示是0x1f 23 02 01,这就是所谓的大端对齐,这种方式又叫作网络字节序。


三 实践例子分析

例子1:

[cpp]  view plain copy
  1. struct A  
  2. {  
  3.     bool a1;  
  4.     bool a2;  
  5.     int a;  
  6.     bool a3;  
  7. };  
  8.   
  9. struct B  
  10. {  
  11.     bool a1;  
  12.     bool a2;  
  13.     bool a3;  
  14.     int a;  
  15. };  
  16.   
  17. struct C  
  18. {  
  19.     bool a1;          
  20.     int a;  
  21.     bool a2;  
  22.     bool a3;  
  23. };  
  24.   
  25. bool t;  
  26.   
  27. printf("Sizeof(bool) = %d,Sizeof(A) = %d,Sizeof(B) = %d,Sizeof(C) = %d\n",sizeof(t),sizeof(A),sizeof(B),sizeof(C));  
答案为:1,12,8,12

例子2:

[cpp]  view plain copy
  1.        struct s1  
  2. {  
  3.     int m1;  
  4.   
  5.     char m2;  
  6.   
  7.     char m3;  
  8. };  
  9.   
  10. struct s2  
  11. {  
  12.     char m2;  
  13.   
  14.     int m1;  
  15.   
  16.     char m3;  
  17. };  
  18.   
  19. struct s3  
  20. {  
  21.     char m2;  
  22.   
  23.     char m3;  
  24.   
  25.     int m1;  
  26. };  
  27.   
  28. printf("Sizeof(s1) = %d,Sizeof(s2) = %d,Sizeof(s3) = %d\n",sizeof(s1),sizeof(s2),sizeof(s3));  
答案为:8,12,8

四 相关分析

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。

其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;2) 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;例如上面第二个结构体变量的地址空间。3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

五 小结与领悟

进行字节对齐的原因是因为处理器在存取内存的时候并不是一个字节一个字节操作的, 通常他会一次存取多个字节, 比如四个。所以将数据以四字节对齐方式存储能够提高数据存取效率(其实具体的存储和对齐方式没那么简单,不说了)。但是, 有时候这种默认的优化并不是我们想要的。比如在设计网络程序的时候,一般情况下我们会定义一个结构体来表示应用程协议的协议头,如果通信双方的程序是在不同体系结构的计算机编译出来的(这很有可能),那么默认的对齐方式是有可能是不同的,这样解析出来的协议头必然就是错的。 另外即使很幸运的对齐方式一样,在协议头里面插入了几个无关的字节那也是很不优雅的何况还占用带宽。

还好,这种对齐方式我们是可以控制的,一般地,可以通过下面的方法来改变缺省的对齐方式:

  · 使用伪指令#pragma pack (n),编译器将按照n个字节对齐;

  · 使用伪指令#pragma pack (),取消自定义字节对齐方式。 

注意:如果#pragma pack (n)中指定的n大于结构体中最大成员的size,则其不起作用,结构体仍然按照size最大的成员进行对界。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值