四头和小端–内部存款和储蓄器对齐难点

是指数据的低位保存在内存的高地址中,         所谓的大端模式,Little-Endian就是低位字节排放在内存的低地址端,Big-Endian就是高位字节排放在内存的低地址端,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送,目前在各种体系的计算机中通常采用的字节存储机制主要有两种,大端存储因为第一个字节就是高位,符号位在最后一个字节

C语言深刻学习连串 – 字节对齐&内部存款和储蓄器处理《转》

         用C语言写程序时索要通晓是多方面形式或然小端方式。

         所谓的多方形式,是指多少的未有保存在内部存储器的高地址中,而数据的高位,保存在内部存款和储蓄器的低地址中;所谓的小端情势,是指数量的比不上保存在内部存款和储蓄器的低地址中,而数据的高位保存在内部存储器的高地址中

 

        
为啥会有大小端方式之分吧?那是因为在计算机体系中,大家是以字节为单位的,每一种地点单元都对应着多少个字节,三个字节为8bit。可是在C语言中除了8bit的char之外,还恐怕有16bit的short型,32bit的long型(要看具体的编写翻译器),别的,对于位数大于8位的计算机,举例14人照旧33人的Computer,由于贮存器宽度大于一个字节,那么必然存在着多个一旦将多少个字节安顿的标题。因而就招致了多方存款和储蓄方式和小端存款和储蓄方式。举个例子八个16bit的short型x,在内部存款和储蓄器中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于多边情势,就将0x11身处低地址中,即0x00第10中学,0x二十二位于高地址中,即0x221第11中学小端形式,刚好相反,依然ox1122。咱们常用的X86结构是小端情势,而KEIL
C51则为多边形式。无数的ARM,DSP都为小端格局。有个别ARM管理器还足以由硬件来挑选是多方面形式或许小端形式。

 

       
Java、.NET大行其道的前天,C语言作为一门经典的高级语言,存在的独一无二理由便是其便捷、精练。随着PC硬件晋级和廉价,C语言由于其自己的复杂度,在支付PC应用软件时,已经比非常少使用。不过在支付嵌入式系统软件和操作系统时,由于广大重申微内核,少占用空间和便捷,因而,在系统开荒舞台上,C语言依然是中流砥柱

     
在事实上的次序支付中,为了巩固多少的读取效能,在内部存款和储蓄器能源丰裕的情事下,一般在定义数据结构时,应该思考四字节对齐,其缘由很轻便,到现在的计算机许多是叁10个人,也正是四字节。在历次读取数据时,一般都是直接读取叁十二位的数据。某个情形下,字节对齐的数据结构,要比非对齐的数据结构占用空间少。以下分别就这两地方举个例子解说。

      1、充分记挂四字节对齐,能够省去存款和储蓄空间

         typedef struct tagAAA{

               char name[10];

               long sno;

               char sex;

               float score[4];

         }AAA;

         typedef struct tagBBB{

               char name[10];

               char sex;

               long sno;

               float score[4];

        }BBB;

       
在VC下,调节和测量检验,能够很轻巧看出来,AAA占的积累空间为36,BBB占的贮存空间为32。原因很简短,在四字节对齐的景况下,按八个字节为单位分配存储空间,如若不足,会自动补充,这一次分配不足以存放上边包车型客车变量时,会重新分配空间。

        AAA:

              |name[0]|name[1]|name[2]|name[3]|

              ————————————

              |name[4]|name[5]|name[6]|name[7]|

              ————————————

              |name[8]|name[9]|             |                   |

                        ———-是因为剩下的五个字节不足以贮存sno(long占多个字节),所以重新分配

              ————————————

              |                       
sno                                       |

                          ———-long变量占八个字节,32bits

              ————————————

              |sex        |    自动填充                                |

                         
———-结余八个字节的空中,不足以重播贰个float变量,由此重新分配

              ————————————

              |                 
score[0]                                     |

              ————————————

              |                 
……….                                        |

              ————————————

              |                 
score[3]                                     |

              ————————————

              
由此能够Infiniti制的计量出,AAA占叁十九个字节,同理,很轻便计算出BBB占33个字节空间。

           

         2、字节对其的情景下,能够更迅捷的探问

               若是三个结构体的多寡如下存储:

              —————————————————–

              |        12        |       34       |        56       
|        78         |   ———–(A)

              —————————————————–  

              —————————————————–

              |        XX        |       YY       |        12      
|         34        |   ———–(B)

              —————————————————–  

              |        56        |        78       |       XX      
|         YY        |

             
在A境况下,贰遍性读取数据成功,但是,在B情形下,必要读取数据两回,由此,可观察成效的分化。

         
一般景色下,字节对齐服从系统字节数与供给的对齐字节数比较,最小原则,即:假如供给按八字节对齐,然则系统为三15人系统,则根据4字节对齐。在四字节对齐时,局地会遵纪守法2字节对齐,如:

             struct tagAAA

             {

                     char a;

                     short b;

                     char c;

             }AAA;

结构体侵吞的半空中为8字节而不是4字节,原因正是:

                    ———————————–

                    |    a     |          |             b              |

                    ———————————–

                    |   c      |                                      |

而不是:

                    ————————————

                    |    a     |          b          |        c        
|

                    ————————————

其缘由正是一些会以2字节对齐

 

***********************************************

 

      
家喻户晓,C语言程序设计中,内部存款和储蓄器的分红和保管完全交由程序猿来决定,因此,内部存款和储蓄器管理是各种C程序员必须熟谙精晓的。一般来讲,分配给进程的内部存储器有四个概念上差别的区域,分别为:代码段、数据段、堆和栈,其中数据段又足以细分为开首化为非零的数额和开端化为零的数额。如下图所示:

           

            ——————-

            |       程序栈         
|———-高地址–〉低地址(向下增进)

            ——————-

            |          堆                |———-迈入增进

            ——————-

            |          BSS              |———-数据段

            | 全局和静态变量 |

            —————————–低地址

            |     可实践代码     |———-代码段

            ——————-

      
可实行命令放在代码段中,任哪天刻,内部存款和储蓄器中唯有一份一样程序的命令拷贝,三个实例分享这几个代码。**早先化为非零的静态数据和全局数据贮存在数额段中,运转同样程序的各样进度,有和煦的数据段。**

       
开始化为零(即未初步化的变量,系统自动填写为0;可能开首化为0)的大局数据和静态分配数据贮存在进程的BSS区域中,种种运转的经过都有投机的BSS,程序运行的时候,将数据放到数据段中,由此可见,独有初步化为非零的变量才占用空间,所以对于类似static
int ss[1024];那样的数组自动用0来填充,它占的半空中极小。

     【BSS是“Block Started by Symbol”的缩写,意为“以符号开首的块”。BSS
是Unix链接器发生的未初步化数据段。别的的段分别是包蕴程序代码的“text”段和包涵已伊始化数据的“data”段。BSS段的变量只知名称和大小
却并未值。此名后来被过多文件格式使用,富含PE。“以符号开首的块”指的是编译器管理未早先化数据的地方。BSS节不带有其余数据,只是简短的掩护开头和了结的地点,以便内部存款和储蓄器区能在运营时被有效地清零。BSS节在应用程序的二进制映象文件中并不真实,举个例子:

  unsigned char var; // 分配到.bss节的8位未初步化变量

  unsigned char var2 = 25; // 分配到.data节的8位已初步化变量**】**

 

     
堆,动态内部存款和储蓄器来自于堆,即:通过malloc得到的上空,平日景况下,堆是升高增加的,即:后边分配的地方比前边的地方在数值上海高校一部分。**【注意:这里的堆而不是数据结构中的堆,它的分红办法邻近于链表】**

 

     
栈,分配局地变量的地点,函数参数、函数的重临值和重回地址也放在栈空间中,需求特别注意的是,当函数再次回到后,存款和储蓄在栈空间中的函数变量“自动消失”,空间被其他函数使用。栈空间是向下加强的。

 

     
在C语言中,一般通过malloc/calloc函数分配空间,通过free()函数释放空间,使用realloc()退换已分配空间的轻重缓急。

      分配内部存款和储蓄器的步骤:

      1.阐澳优(Ausnutria Hyproca)个钦赐项目标指针

      2.企图要分配空间的轻重缓急,一般选拔函数sizeof()来促成

      3.调用函数malloc()完结空间的申请,将函数的回来值赋给指针变量,

      4.检查返回值是或不是不为NULL,有限协助空间分配成功

     
5.分配好的空中是不曾经过起首化的,个中恐怕包涵部分垃圾音信,因而调用函数memset()将其用0来填充是个好的习于旧贯

      释放内部存储器步骤:

      1.调用函数free()释放掉空间

       注意:

        1.不能运用free()之后的空间

        2.free()后,最佳将指针置为NULL,因为假若不做那步管理,**本来的指针如故指向刚才释放的半空中,能够继续操作**

         3.制止再一次释放空间

 

      
Unix系统上,提供了函数alloca()函数,能够兑今后栈空间上分红内定大小的长空,那样的好处是,函数甘休后,空间活动释放,不必显式地调用函数free(),不过该函数有无数弊病,譬如不可移植等,因而不提议使用。

       有不可或缺提一下malloc、calloc、realloc函数的底层完毕,在Linux系统中,提供了brk()和sbrk()函数,上面多少个函数正是在这多个函数的底子上完成的




如何是多方面和小端

        Big-Endian和Little-Endian的概念如下:
1)
Little-Endian就是低位字节排泄在内部存款和储蓄器的洼地址端,高位字节排泄在内部存款和储蓄器的高地址端。
2)
Big-Endian正是高位字节排泄在内部存储器的洼地址端,低位字节排泄在内部存款和储蓄器的高地址端。
举一个例证,比如数字0x12 34 56 78在内部存储器中的表示形式为:

1)大端情势:

低地址 —————–> 高地址
0x12  |  0x34  |  0x56  |  0x78

2)小端形式:

低地址 ——————> 高地址
0x78  |  0x56  |  0x34  |  0x12

足见,大端格局和字符串的储存形式类似。

3)上面是五个具体育赛事例:

16bit宽的数0x1234在Little-endian情势(以及Big-endian情势)CPU内部存储器中的存放方式(假使从地址0x陆仟上马寄放)为:

 

内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x34 0x12
0x4001 0x12 0x34

32bit宽的数0x12345678在Little-endian格局以及Big-endian情势)CPU内部存款和储蓄器中的寄存情势(要是从地址0x四千开端贮存)为:

内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78

 

 4)大端小端未有什么人优什么人劣,各自优势正是对方瑕疵:

小端情势 :强制转换数据无需调治字节内容,1、2、4字节的仓库储存方式同样。
多边形式 :符号位的推断固定为率先个字节,轻松看清正负。

 

大小端方式,端格局

大小端难题至关心重视要涉及的是非曲直单字节非字符串外的别的数据的表示和传递,如short型、int型等。大端和小端有其分其他优势。我们明白Computer符合规律的内部存储器增进格局是从低到高(当然栈不是),取多少方式是从基址依据偏移找到她们的地方,从他们的存款和储蓄格局能够看看,大端存款和储蓄因为第三个字节正是高位,进而很轻巧知
道它是正数照旧负数,对于有个别数值判断会很迅猛。而小端存款和储蓄第二个字节是它的未有,符号位在终极贰个字节,那样在做数值四则运算时从未有每一回抽取相应字节运算,最终直到高位,况兼最后把符号位刷新,这样的运算情势会更高效。

short 可能 long的数据在进展通讯的时候最佳养成:
1、发送的时候使用:htons(l)
2、接受的时候利用:ntohs(l)
而不要理会两边的通讯是还是不是须求这么做~~
自然了貌似小编都休想int型的多寡通信,平昔都以字符串通讯,发送方利用sprintf组织,接收方利用atoi举办调换~~

 


各样Computer种类布局中,对于字节、字等的累积机制有所差别,因此吸引了Computer通讯领
域中五个很主要的标题,即通讯双方交换的音讯单元(比特、字节、字、双字等等)应该以怎么着的逐一实行传递。假诺不达到一致的平整,通讯双方将不能够进展正确的编/译码进而变成通讯退步。近来在各类系统的Computer中国和东瀛常采纳的字节存款和储蓄机制至关心重视要有三种:Big-Endian和Little-Endian,上面先
从字节序谈起。
一、什么是字节序
字节序,从名称想到所满含的意义字节的逐个,再多说两句正是高于一个字节类型的数量在内部存款和储蓄器中的贮存顺序(三个字节的数目当然就无需谈顺序的标题了)。其实许多人在事实上的开荒中都非常少会平素和字节序打交道。独有在跨平台以及网络程序中字节序才是八个应当被思念的题目。

在装有的牵线字节序的稿子中都会涉及字
节序分为两类:Big-Endian和Little-Endian,引用规范的Big-Endian和Little-Endian的概念如下:
a)
Little-Endian就是低位字节排泄在内部存储器的洼地址端,高位字节排泄在内部存款和储蓄器的高地址端。
b)
Big-Endian正是高位字节排泄在内部存款和储蓄器的盆地址端,低位字节排放在内部存款和储蓄器的高地址端。
c)
网络字节序:TCP/IP各层协商将字节序定义为Big-Endian,由此TCP/IP协议中央银行使的字节序通常堪当互连网字节序。

1.1 什么是高/低地址端
首先大家要通晓C程序印象中内部存款和储蓄器的空中布局景况:在《C专
家编程》中恐怕《Unix蒙受高等编制程序》中有有关内部存款和储蓄器空间布局景况的认证,大约如下图:
———————– 最高内部存款和储蓄器地址 0xffffffff
栈底

数组在绝抢先50%小端情形下的囤积:

  以unsigned int value =
0x12345678为例,分别看看在二种字节序下其积存景况,我们得以用unsigned
char buf[4]来表示value:
  Big-Endian: 低地址存放高位,如下:
高地址
        —————
        buf[3] (0x78) — 低位
        buf[2] (0x56)
        buf[1] (0x34)
        buf[0] (0x12) — 高位
        —————
        低地址
Little-Endian: 低地址贮存低位,如下:
高地址
        —————
        buf[3] (0x12) — 高位
        buf[2] (0x34)
        buf[1] (0x56)
        buf[0] (0x78) — 低位
        ————–

低地址

 

原来的小说者小说链接,写得很好,没供给再另行剖析了,看那几个就够了。一般在《电脑组成原理》,或然《微型Computer原理》,或然《汇编语言》等课程中也可以有介绍,可是未有这样详细彻底罢了。浅橙笔记是自家的讲解。

栈顶

怎会有大小端方式之分吧?

     
这是因为在管理器体系中,大家是以字节为单位的,各类地点单元都对应着贰个字节,三个字节为8bit。可是在C语言中除了8bit的char之外,还会有16bit的short型,32bit的long型(要看具体的编写翻译器),别的,对于位数大于8位的微管理器,比方十八个人照旧三十一位的处理器,由于贮存器宽度超过贰个字节,那么一定期存款在着八个只要将多个字节安顿的难题。由此就导致了多方面存款和储蓄方式和小端存储方式。譬如贰个16bit的short型x,在内部存款和储蓄器中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端情势,就将0x十二人于低地址中,即0x00第10中学,0x22坐落高地址中,即0x001第11中学。小端形式,刚好相反。我们常用的X86结构是小端方式,而KEIL
C51则为多边形式。相当多的ARM,DSP都为小端方式。某些ARM管理器仍是能够由硬件来挑选是多方面情势恐怕小端形式。

 


NULL (空洞)

如何决断机器的字节序

能够编写制定七个小的测量检验程序来判别机器的字节序:

 

[cpp] view
plain copy

 

 图片 1图片 2

  1. BOOL IsBigEndian()    
  2. {    
  3.     int a = 0x1234;    
  4.     char b =  *(char *)&a;  //通过将int强制类型转产生char单字节,通过推断初始存款和储蓄地点。即也就是 取b等于a的低地址部分    
  5.     if( b == 0x12)    
  6.     {    
  7.         return TRUE;    
  8.     }    
  9.     return FALSE;    
  10. }  

联手体union的寄存顺序是富有成员都从低地址初步寄存,利用该天性能够轻便地获得了CPU对内部存款和储蓄器采取Little-endian还是Big-endian方式读写

 

[cpp] view
plain copy

 

 图片 3图片 4

  1. BOOL IsBigEndian()    
  2. {    
  3.     union NUM    
  4.     {    
  5.         int a;    
  6.         char b;    
  7.     }num;    
  8.     num.a = 0x1234;    
  9.     if( num.b == 0x12 )    
  10.     {    
  11.         return TRUE;    
  12.     }    
  13.     return FALSE;    
  14. }  

 

先首要介绍了大小端难题的来源于,小编应该是基于叁13个人的机械来说课的。

未伊始 化的数量
———————– 统称数据段

内部存款和储蓄器对齐难题

 

怎么决断内部存款和储蓄器对齐准绳,sizeof的结果怎么来的,请记住以下3条准则:(在并未有#pragma
pack宏的气象下,务不可不看完最终一行)

1:数据成员对齐准则:结构(struct)(或伙同(union))的数额成员,第3个数据成员放在offset为0的地方,未来种种数据成员存款和储蓄的序曲地点要从该成员大小只怕成员的子成员大小(只要该成员有子成员,举个例子身为数组,结构体等)的整好几倍起首(譬喻int在32位机为4字节,则要从4的整数倍地址初阶积累。

2:结构体作为成员:假若一个组织里有好几结构体成员,则结构体成员要从其里面最大体素大小的莫西干发型倍地址初始存储.(struct
a里存有struct b,b里有char,int ,double等因素,那b应该从8的整好数倍开头存款和储蓄.)

3:收尾专门的学问:结构体的总大小,也正是sizeof的结果,.务必是其里面最大成员的大背头倍.不足的要补齐.

[cpp] view
plain copy

 

 图片 5图片 6

  1. typedef struct bb  
  2. {  
  3.  int id;             //[0]….[3]  
  4.  double weight;      //[8]…..[15]      原则1  
  5.  float height;      //[16]..[19],总厅长要为8的卡尺头倍,补齐[20]…[23]     原则3  
  6. }BB;  
  7.   
  8. typedef struct aa  
  9. {  
  10.  char name[2];     //[0],[1]  
  11.  int  id;         //[4]…[7]          原则1   
  12.  double score;     //[8]….[15]      
  13.  short grade;    //[16],[17]          
  14.  BB b;             //[24]……[47]       原则2  
  15. }AA;  
  16.   
  17. int main()  
  18. {  
  19.   AA a;  
  20.   cout<<sizeof(a)<<” “<<sizeof(BB)<<endl;  
  21.   return 0;  
  22. }   

结果是

48 24
ok,上边包车型大巴全看驾驭了,内部存款和储蓄器对齐基本过关.

在各类计算机连串布局中,对于字节、字等的蕴藏机制有所区别,因此引发了微机通讯领
域中一个很注重的主题材料,即通讯双方沟通的音信单元(比特、字节、字、双字等等)应该以什么样的依次举办传递。如若不达到规定的标准一致的准则,通讯双方将不恐怕开始展览正确的编/译码进而致使通讯退步。如今在各个系统的管理器中数见不鲜使用的字节存款和储蓄机制主要有二种:Big-Endian和Little-Endian,下边先从字节序提及。

初步化的多寡

正 文段(代码段)
———————– 最低内存地址 0x00000000
由图能够看到,再内部存储器分布中,栈是向下拉长的,而堆是进化拉长的。
上述图为举个例子果我们在栈 上分红一个unsigned char
buf[4],那么那个数组变量在栈上是何等布局的吧?看下图:

再讲讲#pragma pack().

在代码前加一句#pragma pack(1),你会很欢腾的意识,上边的代码输出为

32 16
bb是4+8+4=16,aa是2+4+8+2+16=32;

那不是名副其实中的未有内部存款和储蓄器对齐的社会风气吗.没有错,#pragmapack(1),告诉编写翻译器,全数的对齐都根据1的板寸倍对齐,换句话说就是未有对齐准则.

知晓了不?

那#pragma pack(2)的结果又是稍稍吧?对不起,5分钟到了,本身去测量试验吧. 

ps:Vc,Vs等编写翻译器暗许是#pragma
pack(8),所以测量检验大家的条条框框会通常;注意gcc暗中认可是#pragma
pack(4),并且gcc只辅助1,2,4对齐。套用三法规里总计的对齐值是不可能当先#pragma
pack指定的n值。

 

一、什么是字节序

栈底 (高地址)

**buf[3]
buf[2]
buf[1]