1. struct A
  2. {
  3. int a;
  4. char b;
  5. short c;
  6. };
  7.  
  8. struct B
  9. {
  10. char a;
  11. int b;
  12. short c;
  13. };
  14.  
  15. #pragma pack(2)
  16. struct C
  17. {
  18. char a;
  19. int b;
  20. short c;
  21. };
  22.  
  23. #pragma pack(1)
  24. struct D
  25. {
  26. int a;
  27. char b;
  28. short c;
  29. };
  30.  
  31. int _tmain(int argc, _TCHAR* argv[])
  32. {
  33.  
  34. cout << sizeof(A) << " "<< sizeof B << " "<< sizeof C << " "<< sizeof D <<endl;
  35. return ;
  36. }
运行结果如下:
8     12     8       7

理论上来说,结构体A与B的大小应该都是一样的,造成这种原因的就是字节对齐引起来的。

2.为什么要字节对齐  

  为什么呢?简单点说:为了提高存取效率。字节是内存空间分配的最小单位, 在程序中,我们定义的变量可以放在任何位置。其实不同架构 的CPU在访问特定类型变量时是有规律的,比如有的CPU访问int型变量时,会从偶数地址开始读取的,int类型占用4个字节(windows平台)。 0X0000,0X0004,0X0008.....这样只需要读一次就可以读出Int类型变量的值。相反地,则需要读取二次,再把高低字节相拼才能得到 int类型的值,这样子看的话,存取效率当然提高了。  通常写程序的时候,不需要考虑这些情况,编译都会为我们考虑这些情况,除非针对那些特别架构的 CPU编程的时候的则需要考虑 。当然用户也可以手工控制对齐方式。

3.编译器对字节对齐的一些规则

我从下面三条说明了编译器对字节处理的一些原则。当然除了一些特殊的编译器在处理字节对齐的方式也不一样, 这些情况我未碰到过,就不作说明了。

  a. 关于数据类型自身的对齐值,不同类型会按不同的字节来对齐。
类型 对齐值(字节)
char 1
short 2
int 4
float 4
double 8
      b. 类、结构体的自身对齐字节值。对于结构体类型与类对象的对齐原则:使用成员当中最大的对齐字节来对齐。比如在Struct A中,int a的对齐字节为4,比char,short都大,所以A的对齐字节为4
     c. 指定对齐字节值。意思是指使用了宏 #pragma pack(n)来指定的对齐值

d. 类、结构及成员的有效对齐字节值。有效对齐值=min(类/结构体/成员的自身对齐字节值,指定对齐字节值)。   

  有效对齐值决定了数据的存放方 式,sizeof 运算符就是根据有效对齐值来计算成员大小的。简单来说, 有效对齐其实就是要求数据成员存放的地址值能被有效对齐值整除,即:地址值%有效对齐值=0

4. 结合编译器分析示例

根据上面的原则,分析Struct A的size。结构体的成员内存分配是按照定义的顺序来分析的。
struct A
{    
   int a;    
   char b;    
   short c;
}  
为了简单起见, 我假设Struct A存取的起始地址为 0x0000 在没有指定对齐值的情况下,分析步骤:  
  1. step : 根据第二条,首先为结构体选择对齐值:选择成员中最大的对齐值,即int a,对齐值为4  
  2. step : 再根据第四条原则,决定有效对齐值:即然没有手工指定对齐值,则使用默认的值:(windows 32平台)   
  3. step : int a 的有效地址值=min(,),(因为0x0000%=),这样a的地址就是从 0X0000~0x0003    
  4. step : char b 的有效对齐值=min(,),地址依次从0x0004 (因为Ox0004%=)开始,分配一个字节,地址段分配情况就是:0x0000~0x0004    
  5. step : short c 的有效对齐值=min(,),理论上说,分配的地址应该是连续的(从0x0005~0x00006),但是由于要求考虑到对齐的情况,所求要求地址段 偏移,这样就从0x0006(Offset+,因为0x0006%=)开始,分配2个字节的地址0x0006~0x0007.
  6. 目前为止,地址段的分配情况就是:0x0000~0x0007这样sizeof(A)的大小=0x0000~0x00078个字节大小,同时,%=0保证了Struct A的地址段与4成偶数倍。

接下来分析Struct B的大小,同样假设Struct B的起始地址为0x0000,分析步骤如下:

struct B
{
    char a;
    int b;
    short c;
}
  1. step : 确实结构体B对齐值:选择成员中最大的对齐值,即int a,对齐值为4
  2. step : 确定手工指定对齐值,使用默认的值:(windows , VC6.0平台)   
  3. step : char a 的有效地址值=min(,),a的地址就是 0X0000(因为0x0000%=) 
  4. step : int b 的有效对齐值=min(,),地址依次从0x0004~0x0007 (因为Ox0004%=)开始,分配4个字节,目前j地址段分配情况就是:0x0000~0x0007    
  5. step : short c 的有效对齐值=min(,),c0x0008~0x0009(因为0x0008%=)开始,偏移2个字节的地址0x0006~0x0007.
  6. 至止,地址段的分配情况就是:0x0000~0x000910个字节,但是Struct B的对齐值为4,这就要求地址地段再偏移2个字节,这样就是从0x0000~0x000B12(因为12%=)个字节大小。这样,sizeof(B)=.

再来使用Pragma手工更改了字节对齐值的情况,先看看Struct C的定义:

#pragma pack(2)
struct C
{
    char a;
    int b;
    short c;
};

在代码中,手工指定了对齐值为2个字节,分析步骤如下:

  1. step : 确定结构体C对齐值:选择成员中最大的对齐值,即int a,对齐值为4   
  2. step : 确定手工指定对齐值,使用手工指定的值:
  3. step : char a 的有效地址值=min(,),(因为0x0000%=),这样a的地址就是0x0000 
  4. step : int b 的有效对齐值=min(,),地址依次从0x0002~0x0005 (因为Ox0002%=)开始,分配4个字节,目前地址段分配情况就是:0x0000~0x0005    
  5. step : short c 的有效对齐值=min(,),由于要求考虑到对齐的情况,从0x0006(因为0x0006%=)开始,分配2个字节的地址0x0006~0x0007
  6. 目前为止,地址段的分配情况就是:0x0000~0x00078个字节,同时也保证了Struct C的对齐情况(2字节对齐,pragma()),sizeof(C)=.

请注意这种情况与Struct B的情况有区别,B的sizeof大小为12个字节,C的sizeof大小为8个字节。

  最后分析#pragma pack(1)这种情况,这种情况非常简单,对齐值为1,因为1可以被任何数据整除,所以Struct D的成员变量存取顺序是连续的,这样就好办了,sizeof(D)=sizeof(int)+sizeof(char)+sizeof(short)=4+1+2=7 (比如从0x0000~0x0006)

总结

  在考虑字节对齐时要细心,搞清楚几个重要的概念,如类型自身对齐值,手工对齐值以及有效对齐值,有效对齐值决定了最后的存取方式,有效对齐值等于类型自身对齐值与手工对齐值中较小的一个。理解了这一点,对sizeof运算符对类型或都结构的运算也彻底明白了。

以下测试实例 -------------------------------------------------------------------------------------------------

  1. #include <iostream>
  2. using namespace std;
  3. // 没有指定对齐字节,则使用 结构体或类 中字节最大的类型字节值(前提是不超过 8 字节,当超过8字节,则采用4字节对齐)
  4. struct A // 8 ->(8%4 == 0)-> 8
  5. {
  6. int a; //
  7. char b;
  8. short c;
  9. };
  10.  
  11. struct B // 10 ->(10%4 != 0)-> 12
  12. {
  13. char a;
  14. int b; //
  15. short c;
  16. };
  17.  
  18. struct C // 12 ->(12%8 != 0)-> 16
  19. {
  20. double b; //
  21. char a;
  22. short c;
  23. };
  24.  
  25. struct D // 18 ->(18%8 != 0)-> 24
  26. {
  27. char a;
  28. double b; //
  29. short c;
  30. };
  31.  
  32. struct E // 20 ->(20%4 == 0)-> 20
  33. {
  34. char a;
  35. long double b; // (12 > 8) -> 采用 4 字节对齐, 和 #pragma pack(4) 效果一样
  36. short c;
  37. };
  38.  
  39. struct F // 16 ->(16%4 == 0)-> 16
  40. {
  41. long double b; //
  42. char a;
  43. short c;
  44. };
  45.  
  46. #pragma pack(4) // 和 struct E 效果一样
  47. struct G // 20 ->(20%4 == 0)-> 20
  48. {
  49. char a;
  50. long double b; //
  51. short c;
  52. };
  53.  
  54. #pragma pack(2) // 会影响后续类型的对齐有效值,直到重新设置
  55. struct H // 8 ->(8%2 == 0)-> 8
  56. {
  57. char a;
  58. int b;
  59. short c;
  60. };
  61.  
  62. //#pragma pack(2)
  63. struct I // 12 ->(12%2 == 0)-> 12
  64. {
  65. char a;
  66. double b; //
  67. short c;
  68. };
  69.  
  70. #pragma pack(1)
  71. struct J // 7 ->(7%1 == 0)-> 7
  72. {
  73. int a;
  74. char b;
  75. short c;
  76. };
  77.  
  78. int main()
  79. {
  80. long double bb;
  81. cout << sizeof(bb) << endl; // 12
  82. // 输出结果为: 8 12 16 24 20 16 20 8 12 7
  1. cout << sizeof(A) << " "<< sizeof(B) << " "<< sizeof(C) << " "<< sizeof(D) << " "<< sizeof(E) << " "<< sizeof(F) << " "<< sizeof(G) << " "<< sizeof(H) << " "<< sizeof(I) << " "<< sizeof(J) << endl;
  2. return ;
  3. }

C++中的字节对齐分析的更多相关文章

  1. C语言中的字节对齐以及其相关处理

    首先,我们来了解下一些基本原理: 一.什么是字节对齐一个基本类型的变量在内存中占用n个字节,则该变量的起始地址必须能够被n整除,即: 存放起始地址 % n = 0,那么,就成该变量是字节对齐的;对于结 ...

  2. C语言字节对齐分析

    1.前言 什么是字节对齐呢?现代计算机中的内存空间都是按字节(byte)划分的,从理论上讲似乎任何类型的变量的访问都可以从任何地址开始,但是实际情况是在访问特定变量的时候经常需要在特定的内存地址进行访 ...

  3. ACE的CDR中的字节对齐问题

    大家应该都知道计算机中间都有字节对齐问题.CPU访问内存的时候,如果从特定的地址开始访问一般可以加快速度,比如在32位机器上,如果一个32位的整数被放在能被32模除等于0的地址上,只需要访问一次,而如 ...

  4. C++中的字节对齐

    本博客(http://blog.csdn.net/livelylittlefish)贴出作者(三二一.小鱼)相关研究.学习内容所做的笔记,欢迎广大朋友指正! 字节对齐 1. 基本概念字节对齐:计算机存 ...

  5. C语言中的字节对齐

    下面这个篇博客讲解很好 http://blog.csdn.net/meegomeego/article/details/9393783 总的来看分三类: 1. 不加 #pragma pack(n)伪指 ...

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

    工作中面试中对于字节对齐基本上是必考一个知识点,而很多面试是网络上上原题.基本上背一背就可以写正确,而关于4字节对齐我相信很多人也只是一个基本地了解,对于一些题目就感觉有问题,而且很多blog后面仍然 ...

  7. c++内存中字节对齐问题详解

    一.什么是字节对齐,为什么要对齐?    现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址 ...

  8. stm32中使用#pragma pack(非常有用的字节对齐用法说明)

    #pragma pack(4)   //按4字节对齐,但实际上由于结构体中单个成员的最大占用字节数为2字节,因此实际还是按2字节对齐 typedef struct { char buf[3];//bu ...

  9. 关于C语言中结构体中的结构体成员导致的字节对齐问题

    关于结构体的字节对齐是什么,就不赘述,再此附上一篇文章,介绍字节对齐:http://www.linuxsong.org/2010/09/c-byte-alignment/ 这里的结构体字节对齐的数据类 ...

随机推荐

  1. Django 缓存、信号

    Reference: http://www.cnblogs.com/lianzhilei/p/6365877.html 缓存 由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访 ...

  2. Java调用存储过程小结

    学生在学习jdbc的时候,会问到怎么调用存储过程,现在将java调用oracle存储过程的示例总结如下.(关于调用sqlserver的存储过程将在下次进行小结请关注) 一:无返回值的存储过程 存储过程 ...

  3. silverlight调用WebService传递json接收绑定数据

    1.接收数据: WebService通过接口接收数据.类型为object 2.类型转换: 通过json转换方法将object转换为json格式数据 3.调用WebService方法: silverli ...

  4. Qt5 can't find -LGL

    一周前新建了一个有关Ubuntu QT5 的虚拟机开发环境,在官网上下载了一个完整的run包,然后编译的时候发现如下错误 can't find -LGL 解决方式: sudo apt-get inst ...

  5. pthread_setschedparam

    #include <stdlib.h> #include <pthread.h> #include <stdio.h> #include <sched.h&g ...

  6. lua中table如何安全移除元素

    在Lua中,table如何安全的移除元素这点挺重要,因为如果不小心,会没有正确的移除,造成内存泄漏. 引子 比如有些朋友常常这么做,大家看有啥问题 将test表中的偶数移除掉local test = ...

  7. js对数字的校验

    //-----------------------------------------------函数(1):允许输入正数和负数的表达式-------------------------------- ...

  8. 5个界面效果很炫的JavaScript UI 框架

    Web 开发很简单,Web 应用的 UI 开发却很复杂,如果能够利用一些成熟的 UI 框架可以大大的减少开发的复杂度.本文向你介绍 5 款界面效果很不错的 JS 框架. 1. ExtJS ExtJS ...

  9. VS2008兼容安装

    1. 直接安装出现问题:该项目的所有配置都需要系统提供对某些 平台的支持,但在此计算机上没有安装这些平台.因此无法加载该项目. 解决方法:先卸载原来安装的, 再设置安装包中的setup.exe的兼容性 ...

  10. e675. 翻转缓冲图像

    // To create a buffered image, see e666 创建缓冲图像 // Flip the image vertically AffineTransform tx = Aff ...