C++中的字节对齐分析

C++中的字节对齐分析

  阅读指引:     

  1.  示例代码

  2. 为什么要字节对齐

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

  4. 结合编译器原则分析示例

  5. 总结

  1.示例代码   

  先看一下这段程序的运行结果。

示例代码
  运行结果如下:
8       12       8         7

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

  2.为什么要字节对齐  

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

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

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

  a. 关于数据类型自身的对齐值,不同类型会按不同的字节来对齐。
类型对齐值(字节)
char1
short2
int4
float4
double4
      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 在没有指定对齐值的情况下,分析步骤:  
复制代码
   step  1 : 根据第二条,首先为结构体选择对齐值:选择成员中最大的对齐值,即int a,对齐值为4
      
   step 
2 : 再根据第四条原则,决定有效对齐值:即然没有手工指定对齐值,则使用默认的值: 4 (windows 32平台)   

    step 
3 int  a 的有效地址值 = min( 4 , 4 ),(因为0x0000 % 4 = 0 ),这样a的地址就是从  0X0000 ~ 0x0003     

    step 
4 char  b 的有效对齐值 = min( 1 , 4 ),地址依次从0x0004 (因为Ox0004 % 1 = 0 )开始,分配一个字节,地址段分配情况就是: 0x0000 ~ 0x0004     

    step 
5 short  c 的有效对齐值 = min( 2 , 4 ),理论上说,分配的地址应该是连续的(从0x0005 ~ 0x00006 ),但是由于要求考虑到对齐的情况,所求要求地址段 偏移,这样就从0x0006(Offset + 1 ,因为0x0006 % 2 = 0 )开始,分配2个字节的地址0x0006 ~ 0x0007 .

     
   目前为止,地址段的分配情况就是:
0x0000 ~ 0x0007这样sizeof(A)的大小 = 0x0000 ~ 0x0007共8个字节大小,同时,8%4=0保证了Struct A的地址段与4成偶数倍。
复制代码

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

struct  B
{
    
char  a;
    
int  b;
    
short  c;
}
复制代码
   step  1 : 确实结构体B对齐值:选择成员中最大的对齐值,即int a,对齐值为4
      
   step 
2 : 确定手工指定对齐值,使用默认的值: 4 (windows  32 , VC6.0平台)   

   step 
3 char  a 的有效地址值 = min( 1 , 4 ),a的地址就是  0X0000 (因为0x0000 % 1 = 0 )   

   step 
4 int  b 的有效对齐值 = min( 4 , 4 ),地址依次从0x0004 ~ 0x0007  (因为Ox0004 % 1 = 0 )开始,分配4个字节,目前j地址段分配情况就是: 0x0000 ~ 0x0007     

    step 
5 short  c 的有效对齐值 = min( 2 , 4 ),c从0x0008 ~ 0x0009 (因为0x0008 % 2 = 0 )开始,偏移2个字节的地址0x0006 ~ 0x0007 .
    
    至止,地址段的分配情况就是:
0x0000 ~ 0x0009共10个字节,但是Struct B的对齐值为4,这就要求地址地段再偏移2个字节,这样就是从0x0000 ~ 0x000B共12(因为12 % 4 = 0 )个字节大小。这样, sizeof (B) = 12

     
   
复制代码

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

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

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

复制代码
   step  1 : 确定结构体C对齐值:选择成员中最大的对齐值,即int a,对齐值为4
      
   step 
2 : 确定手工指定对齐值,使用手工指定的值: 2

   step 
3 char  a 的有效地址值 = min( 1 , 2 ),(因为0x0000 % 2 = 0 ),这样a的地址就是0x0000 

   step 
4 int  b 的有效对齐值 = min( 4 , 2 ),地址依次从0x0002 ~ 0x0005  (因为Ox0002 % 2 = 0 )开始,分配4个字节,目前地址段分配情况就是: 0x0000 ~ 0x0005     

    step 
5 short  c 的有效对齐值 = min( 2 , 2 ),由于要求考虑到对齐的情况,从0x0006(因为0x0006 % 2 = 0 )开始,分配2个字节的地址0x0006 ~ 0x0007

     
   目前为止,地址段的分配情况就是:
0x0000 ~ 0x0007共8个字节,同时也保证了Struct C的对齐情况(2字节对齐,pragma(2)), sizeof (C) = 8
复制代码

  请注意这种情况与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运算符对类型或都结构的运算也彻底明白了。



关于C++字节对齐问题

关于C/C++的字节对齐

这两天写解析SWF文件的程序,在结构体指针和从文件中读出来的进行转换的时候遇到一些问题,就是有一个struct A,例如:

struct A

{

         char flag;

         int length;

         int id; 

         ....

};

然后一个飘逸的 struct A *a = (struct A*)buffer;   // 世界一下清静咯

可惜在输出a中的成员的时候却发现老是不正确,百思不得起解。今晚一想,可能是由于C++的字节对齐问题,所以直接就百度了一些

C++的字节对齐。看到下面这篇文章说的挺清晰的,就转了过来。

________________________________________________________________________________________________________

关于C/C++的字节对齐

最近研究C++的数据类型和数据大小时发现,字节对齐实际上是一个C/C++程序员无法忽视的一个问题.所以进行了初步的研究.

1.为什么会出现内存对齐问题-从x86 CPU架构说起
相信绝大多数计算机系和软件学院的学生都学过计算机组成原理这门基础课程,所以不作入门引导了,没有相关背景的可以补补相关课程资料.常有人问我,既然是按字节寻址方式,为何会出现字节对齐问题呢?难道是内存不是连续的吗?
这个问题实际上和CPU与内存的连接方式有关,我们知道CPU会通过地址总线Address BUS与CPU连接用于寻址,用Data Bus数据总线连接用于获取数据,而内存通常是RAM构成的复杂阵列。

对于这个RAM阵列(实际内存可能更复杂,但是原理类似), 前16个内存地址排列对应内存单元关系如下:



对照上图,是不是发现,对于每个内存操作周期,每个地址实际上可以读取4个bytes的数据呢,那么假设你有一个int型的数据存储在地址1或者2或者 3,那么,是不是一个CPU内存时钟周期不够取出这个数据呢?这样是不是效率会很低下?这也就是为什么我们需要内存对齐了,虽然现代CPU和编译器已经对 此做了很多优化,但是C/C++由于特殊性,程序员必须了解这个细节.

2.C/C++内存对齐的方式
C/C++中的结构/类的成员变量在对齐在内存中的排列是与它们摆放的先后顺序相关的,先看看下面的结构

1 struct malign_a
2 {
3    char a;
4    short b;
5    char c;
6    int d;
7 };

这个结构的sizeof(malign_a)是多少呢?1+2+1+4 =8?吗?这个实际上不对,那么,如何对齐的呢?
由于char是1个byte,所以不管他在什么位置,都可以用一个内存周期读出数据,short是半个字也就是2 bytes,这个时候假如它的地址是某个边界位置上,那么,也需要两个内存周期来读取,以此类推.所以,内存对齐就是加入填充padding无意义的数据 来保证某个数据位于一个可以通过最少内存周期的位置,比如double型只有地址位于%4等于0的位置,才能保证2个周期读出.

同时对于每个具体平台,不同的编译器有不同的指定的对齐模式,比如C/C++可以通过

1 #pragma pack(n) //n为1,2,4,8,16等

来指定对齐.当然,一般x86 32位机器下,都是默认4字节对齐的.这个n也称为对齐模数.
对齐策略如下:

1.结构体成员对齐:按某成员数据本身大小以及指定大小中较小者对齐
2.结构体整体对齐:按所有成员中最大者和对齐模数中较小者对齐

所以上述结构体按默认对齐方式如下:

1.a是char类型,可以在任意位置,位于位置0
2.b是short类型,2比默认的4小,所以按2字节对齐,所以在a后面填充1个字节,b必须位于位置2,占两个字节
3.c是char类型,位于位置4
4.d是int类型,4<=4,所以按4字节对齐,需要在c后面填充3个字节,位置为8
5.由于最大成员是4,默认是4,所以结构已经按4对齐,所以总字节数为12


再把原来的结构体成员位置改动一下:

1 struct malign_b
2 {
3    char a;
4    char c;
5    short b;
6    int d;
7 };

这个结构的大小为8,对齐过程如下:

1.a是char类型,可以在任意位置,位于位置0
2.c是char类型,位于位置1
3.b是short类型,2比默认的4小,所以按2字节对齐,这时候恰好位于位置2
4.d是int类型,4 5.由于最大成员是4,默认是4,所以结构已经按4对齐,所以总字节数为8


最后为了加深理解,我们来看看下面这个结构按对齐模数2对齐时候的memory layout

01 #pragma pack(push)
02 #pragma pack(2)
03 struct malign_b
04 {
05    char a;
06    int d;
07    char c;
08    short b;
09 };
10 #pragma pack(pop)

它的大小为10,当指定为对齐模数2时候

1.由于a是char,1<2,位于位置0
2.由于d是int,4>2,所以按2字节对齐,所以必须在a后面填充一个字节,位于位置2,
3.c是char,位置为6
4.b是short,所以必须在c后面填充一个字节,位置为8
5.最大的是4,所以按2对齐,上述结果其实已经整体是按2对齐,所以总数为10


我相信通过以上例子,应该可以熟悉字节对齐过程了,如果有不妥之处,敬请留言指出,谢谢.
原文链接: http://www.cnblogs.com/shihao/archive/2012/01/18/2325580.html


微软C编译器(cl.exe for 80×86)的对齐策略: 
第一: 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。 
第二:结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internaladding); 
备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。 
第三: 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailingpadding)。 
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
填充字节就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。对于结构体本身也存在着对齐要求,ANSIC标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,但是可以更严格(但此非强制要求,VC7.1就仅仅是让它们一样严格)。C标准保证,任何类型(包括自定义结构类型)的数组所占空间的大小一定等于一个单独的该类型数据的大小乘以数组元素的个数。换句话说,数组各元素之间不会有空隙。

总结规则如下: 
0: 结构体变量的首地址能够被其最宽基本类型成员的大小所整除 
1: VC6和VC71默认的内存对齐方式是 #pragam pack(8) 
2:结构体中每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中较小的一个对齐. 
3: 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍. 
4: 结构体本身也存在着对齐要求规则,不能比它所有字段中要求最严格的那个宽松. 
5: 结构体的总大小为结构体最宽基本类型成员大小的整数倍,且应尽量节省内存。 
6: 在GCC中,对齐模数的准则是:对齐模数最大只能是4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。

  1. 而且在上述的规则中,第3条里,offset必须是成员大小的整数倍:
  2. (1): 如果这个成员大小小于等于4则按照上述准则是可行的,
  3. (2): 如果成员的大小大于4,则结构体每个成员相对于结构体首地址的偏移量只能按照是4的整数倍来进行判断是否添加填充。

位域情况: 
C99规定int、unsignedint和bool可以作为位域类型。但编译器几乎都对此作了扩展,允许其它类型类型的存在。 
如果结构体中含有位域(bit-field),总结规则如下 
1)如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止; 
2)如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍; 
3)如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式; 
4)如果位域字段之间穿插着非位域字段,则不进行压缩; 
5) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,且应尽量节省内存。 
备注:当两字段类型不一样的时候,对于不压缩方式,例如:

  1. structN{
  2. charc:2;
  3. int i:4;
  4. };

依然要满足不含位域结构体内存对齐准则第3条,i成员相对于结构体首地址的偏移应该是4的整数倍,所以c成员后要填充3个字节,然后再开辟4个字节的空间作为int型,其中4位用来存放i,所以上面结构体在VC中所占空间为8个字节; 
而对于采用压缩方式的编译器来说,遵循不含位域结构体内存对齐准则第3条,不同的是,如果填充的3个字节能容纳后面成员的位,则压缩到填充字节中,不能容纳,则要单独开辟空间,所以上面结构体N在GCC所占空间应该是4个字节。


C++ 字节对齐

1. 缘起

    来自BBS上的面试题目,struct{int a; char b;}的大小是多少?答案是8。上网看了下,是字节对齐。

2. 字节对齐的基本规则

    首先,每种类型的变量的默认对齐长度都是自己的变量长度,比如:char占一个字节,那么对齐长度就是一个字节,int占四个字节,对齐长度就是四个字节,double占八个字节,对齐长度就是8。int的对齐长度为4的实际意义是,int变量必须存储在四的倍数的地址上。
    那么对于struct{char b; int a},其长度是8,因为b虽然只占用1个字节,但是a必须从4的倍数开始存储,因此b后面的3个字节都废掉了。因此一共需要8个字节才能把b和a存下来。
    那么对于struct{int a; char b},其长度还是8!晕菜了!原因如下:
    字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
   1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
   2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
   3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
   规则1是控制结构体变量的首地址的,与结构体变量的长度没关系。
   规则2是控制结构体内每个变量的相对地址的,与结构体变量的长度有关系。
   规则3是控制结构体总体长度的,与结构体变量的长度有关系。
   正是由于第三条规则,结构体的长度必须是其最长的变量长度的整数倍,因此在上面的例子中,必须是4的整数倍,因此,是8。
   如果结构体里面嵌套结构体就要注意了,结构体变量的起始地址只是其内部最宽的基本类型的整数倍,而非结构体自身的整数倍,外面结构体的长度,也仅仅是里面最宽的基本类型的长度倍数。
   比如: 

复制代码
struct  S1 {
  
char  c;   //  1个字节
   int  i;    //  前面空3个字节,占用4个字节
};  //  刚好8个字节,是4的倍数 
struct  S2 { 
  
char  c1;  //  1个字节
  S1 s;   //  前面空3个字节,而不是空7个字节,占用8个字节
   char  c2;  //  占用1个字节 
};  //  一共13个字节,要成为4的倍数,后面增加3个字节,成为16个字节
复制代码

3. 字节对齐的原因

   ·效率原因,某些平台每次都是从偶地址读数据,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。

4. 字节对齐的隐患

   ·不合理设置变量定义的顺序,可能浪费内存   

复制代码
struct  S1 {  //  长度为16
     char  a;
    
int  b;
    
char  c;
    
int  d;
}
struct  S2 {  //  长度为12
     char  a;
    
char  c;
    
int  b;
    
int  d;   
}
复制代码

   ·更多的是在对地址运算时,没有考虑的字节对齐问题

char  a;
int  b;
int   * pb  =   & a + 1 // 这样a的地址加1,并不是b的地址,实际上,这行代码,在编译时就出现了ERROR,因为不能把char *的地址赋值给int *的变量

5. 参考文章

结构体字节对齐问题  http://blogold.chinaunix.net/u1/49467/showart_424793.html
字节的对齐原理 http://zhengyueyatou.blog.sohu.com/64814717.html


我们知道的当一个结构体,如果我们想知道他占多少内存空间的话我们可以利用sizeof()来查看,但是有时候sizeof()得出的结构竟然和我们想象的不一样,这和编译器有很大的关系。编译器有自己的字节对齐机制,他为了某些原因,在内存上进行了一些调整。和填充,导致得到了与我们想法不一样的结果。虽然vc字节对齐网上有很多资料,但是呢,下面我就说说自己的理解,自己对这套规则的记忆方法。


采用字节对齐的原因:
         各种平台在内存处理上有很大不同,存取方式也差别很大。很多平台为了提高自己数据的存取效率,都会充内存上对数据进行一些调整,如数据存放进行对齐,以便提高自己生的数据存取速度,提高程序执行效率。

字节对齐规则:

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding); 
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

实例说明:

假如我们定义了一个结构体 

  1. typedef struct tagHeader{  
  2.             int nLen;  
  3.             char type;  
  4.             short one;  
  5.         }Header,*pHeader;  

按照我们正常的思路  sizeof(Header) = 4(int)+1(char)+2(short ) 也就是7个字节  但是事实上呢,你如果运行一下你会发现

sizeof(Header) = 8 为什么会这样呢?原因就是上面所说的字节对齐造成的,那么现在我们就按照上面所说的字节对齐的规则来,看一下他到底是 怎么进行对齐的

1.首先我们来 找出 在这个结构体里面最大的基本成员变量空间是int 他占4个字节.

2.然后我们按照结构体内部变量定义顺序(既是 int-char-short)来分析

(1)int nlen 相对于结构体的起始地址0, 他是4的整数倍,满足字节对齐的条件2

(2)第二个变量char type 相对于结构体的其实地址是4,他是1的整数倍,显然也满足字节对齐规则的条件2

(3)第三个变量 short one 相对于结构体的起始地址是5(0+4+1) 显然这不是2的整数倍,不满足对齐规则条件2,于是这时候就要采取字节对齐了,系统为了让他满足字节对齐,会自动填充一个字节使其的其实地址变为6 ,这样便可以整除 2 了,所以one的起始地址就变为了6 这时就满足对齐规则条件2.

3.最后我们来看看 结构体总共占的大小 4(int)+1(char)+1(系统自动填充)+2(short) =8  而8恰好是4(该结构体最大的基本类型是int)的倍数 满足条件3,因此该结构体真正的大小就为8.

内存结构如下图一

下面我们再来看一个例子,我们仅仅把结构体内容调换一下

  1. typedef struct tagHeader{  
  2.             char type;  
  3.             int nLen;  
  4.             short one;  
  5.         }Header,*pHeader;  
同上面一个例子一样的分析方法

1.首先找出 在这个结构体里面最大的 基本成员变量是int 他占4个字节.

2.然后我们按照结构体内部变量定义顺序(既是 char-int-short)来分析

          (1)第一个变量char type 相对于结构体的起始地址0, 他的起始地址是1的整数倍,满足字节对齐的条件2

          (2)第二个变量 int nlen相对于结构体的起始地址是1,他的起始地址不是4的整数倍,不满足字节对齐的条件2,因此要进行字节填充。为了成为4的倍数,所以要填充3个字节。

(3)第三个变量 short one 相对于结构体的起始地址是8(0+1+3+4) 是2的整数倍.满足字节对齐的条件2。

3.最后我们来看看 结构体总共占的大小 1(char)+3(系统自动填充)+4(int)+2(short) =10  而10不是4(该结构体最大的基本类型是int)的倍数 不满足满足条件3,因此必须再次进行填充,使整个结构体的大小满足4的倍数,因此还要填充2个字节 故 最终字节应该是12.

   内存结够如下图二

最后我们再来看一个复合的结构体

  1. typedef struct tagHeader{  
  2.             char type;  
  3.             int nLen;  
  4.             short one;  
  5.         }Header,*pHeader;  
  6.   
  7. typedef struct tagTest{  
  8.     char type;  
  9.     Header head;  
  10. }TEST,*LPTEST;  

仍然按照我们自己的步骤来 分析

1.首先找出 在这个结构体里面最大的 基本类型成员变量是int 他占4个字节(Header 不是基本类型).

2.然后我们按照结构体内部变量定义顺序(既是 char-header)来分析

          (1)第一个变量char type 相对于结构体的起始地址0, 他的起始地址是1的整数倍,满足字节对齐的条件2

          (2)第二个变量 Header相对于结构体的起始地址是1,他的起始地址不是4的整数倍(这里是最大基本类型成员变量是int),不满足字节对齐的条件2,因此要进行字节填充。为了成为4的倍数,所以要填充3个字节。

(3)按照我们分析的前面分析的方法分析结构体的head的内存情况。

3.最后我们来看看 结构体总共占的大小 1(char)+3(系统自动填充)+10(结构体) =14  而14不是4(该结构体最大的基本类型是int)的倍数 不满足满足条件3,因此必须再次进行填充,使整个结构体的大小满足4的倍数,因此还要填充2个字节 故 最终字节应该是16.

内存结构如下图三

                                                                              内存结构图解

以上便是字节对齐的情况。

字节对齐是提高了数据存取熟读,提高程序的效率,但是有些时候我们的程序不需要字节对齐。比如我们要将一个结构体发送出去,发送到网络中的另一台计算机上,但是这台计算机采用的平台和我们开发的平台却不一样。假若我让系统字节对齐的话,但接受方由于平台和我们不一样,这样很可能就导致对方解析数据不正确,所以有时候不能使用字节对齐。可那么假如我不想让系统字节对齐,或者说我们想自己指定对齐字节是多少,那我们该怎么办?

C++便可以这样 采用#pragma pack 命令, 当你定义结构体时这样定义。

  1. #pragma pack(push,1) //表示已1字节对齐  
  2. typedef struct tagHeader{  
  3.             char type;  
  4.             int nLen;  
  5.             short one;  
  6.         }Header,*pHeader;  
  7. #pragma pack(pop)  
或者这样也行
  1. #pragma pack(push)  
  2. #pragma pack(1)//表示以一字节对齐  
  3. typedef struct tagHeader{  
  4.             char type;  
  5.             int nLen;  
  6.             short one;  
  7.         }Header,*pHeader;  
  8. #pragma pack(pop)  

#pragma pack(push)和#pragam pack(pop) 之间的结构体就采用一字节对齐,如果你想以其他字节对齐,自己修改#pragma中的数字便是,如以4字节对齐 就改为

#pragma pack(4) 这样便可以了。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值