大小端模式详解

int i=1;  

char *p=(char *)&i;      
if(*p==1)                printf(“1”);      else           
printf(“2”);

         
大小端存储问题,如果小端方式吃(i占至少少个字节的长短)则i所分配的内存最小地方那个字节中就是存正1,其他字节是0.大端的言辞则1于i的嵩地址字节处存放,char是一个字节,所以强制将char型量p指向i则p指向的得是i的低地址,那么就是可以判明p中的价值是休是1来规定是免是小端。

 

请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1

解答:

int checkCPU( )

{

    {

           union w

           { 

                  int a;

                  char b;

           } c;

           c.a = 1;

           return(c.b ==1);

    }

}

剖析:

嵌入式系统开发者应该本着Little-endian和Big-endian模式大了解。采用Little-endian模式的CPU对操作数的存放方式是自低字节至高字节,而Big-endian模式对操作数的寄放方式是由高字节到低字节。例如,16bit宽之数0x1234以Little-endian模式CPU内存中的存方式(假设从地址0x4000初始存放)为:

内存地址
0x4000
0x4001
存放内容
0x34
0x12

设若以Big-endian模式CPU内存中的存方式尽管也:

内存地址
0x4000
0x4001
存放内容
0x12
0x34

32bit宽之数0x12345678每当Little-endian模式CPU内存中的存方式(假设从地址0x4000起来存放)为:

内存地址
0x4000
0x4001
0x4002
0x4003
存放内容
0x78
0x56
0x34
0x12

要是于Big-endian模式CPU内存中的存方式尽管为:

内存地址
0x4000
0x4001
0x4002
0x4003
存放内容
0x12
0x34
0x56
0x78

一同体union的存顺序是兼备成员还从低地址开始存放,面试者的解答用该特性,轻松地获取了CPU对内存以Little-endian还是Big-endian模式读写。如果哪个能现场让有此解答,那直就是是一个天资的程序员。

 

 

补充:

所谓的绝大部分模式,是乘多少的小(就是权值较小之末尾那几各项)保存在内存的赛地址被,而数据的要职,保存在内存的低地址中,这样的仓储模式来三三两两类似于把多少当作字符串顺序处理:地址由小为大益,而数从高位往低放;
   所谓的小端模式,是依靠多少的不如保存在内存的低地址中,而往往
据的高位保存在内存的大地址被,这种囤模式将地方之轻重与数码位权有效地做起来,高地址有权值高,低地址有权值低,和我们的逻辑方式同样。
  
为什么会出大小端模式的分吧?这是坐以处理器体系遭到,我们是盖字节为单位的,每个地点单元都对准诺在一个字节,一个字节为
8bit。但是当C语言中除了8bit的char之外,还出16bit之short型,32bit之long型(要拘留现实的编译器),另外,对于位数大于
8员的计算机,例如16位还是32位之电脑,由于寄存器宽度超过一个字节,那么得在正在一个若拿多独字节安排的题目。因此即使造成了多头存储模式和小
端存储模式。例如一个16bit之short型x,在内存中之地址为0x0010,x的值为0x1122,那么0x11啊高字节,0x22啊没有字节。对于
大端模式,就以0x11厕低地址中,即0x0010中,0x22位居高地址被,即0x0011中。小端模式,刚好相反。我们常因此的X86结构是小端模
式,而KEIL
C51则为多边模式。很多底ARM,DSP都也小端模式。有些ARM处理器还好由硬件来选是多方面模式或者小端模式。

 

  
下面这段代码可以为此来测试一下你的编译器是多方面模式或小端模式:

 

short int x; char x0,x1; x=0x1122;
x0=((char*)&x)[0]; //低地址单元 x1=((char*)&x)[1]; //高地址单元
若x0=0x11,则是多方面; 若x0=0x22,则是小端……
上面的先后还足以望,数据寻址时,用底是亚字节的地点。

 


嗬时要拓展高低端字节序的换?  

short 或者 long的数目在进行通信的时节最好好养成:
 1、发送的时刻以:htons(l)  2、接受的当儿下:ntohs(l)
 而不用理会两边的通信是否要这么做~~
 当然了相似自己都并非int型的数码通信,从来还是字符串通信,发送方利用sprintf组织,接收方利用atoi进行转换~~


 

掬模式(Endian)的这个词出自Jonathan
Swift书写的《格列佛游记》。这仍开根据将鸡蛋敲起之艺术不同将具有的口分成两类,打圆头开始拿鸡蛋敲起之总人口深受归为Big
Endian,从终端开始以鸡蛋敲起之人头叫归为Littile
Endian(这词话不过形象)。
小人国的内战就源于吃鸡蛋时凡究竟从大洋(Big-Endian)敲起还是从小头(Little-Endian)敲起。在计算机业Big
Endian和Little
Endian也几引起一集战争。在计算机业界,Endian代表数据以存储器中之存放顺序。下文举例说明以电脑中大小端模式之别。

倘用一个32各类的整数0x12345678存放到一个整型变量(int)中,这个整型变量采用大端或者小端模式在内存中之仓储由下表所示。为简便起见,本文使用OP0表示一个32员数据的万丈字节MSB(Most
Significant Byte),使用OP3表示一个32号数据低于字节LSB(Least
Significant Byte)。

 

地址偏移

大端模式

小端模式

0x00

12(OP0)

78(OP3)

0x01

34(OP1)

56(OP2)

0x02

56(OP2)

34(OP1)

0x03

78(OP3)

12(OP0)

 

小端:较高之行字节存放在比高的之存储器地址,较逊色之中字节存放在比逊色的存储器地址。
大端:较高之可行字节存放在比逊色的存储器地址,较逊色之有效性字节存放在比高的存储器地址。
倘若以一个16员的整数0x1234存到一个短整型变量(short)中。这个短整型变量在内存中之蕴藏在大小端模式由于下表所示。

 

地址偏移

大端模式

小端模式

0x00

12(OP0)

34(OP1)

0x01

34(OP1)

12(OP0)

 

鉴于上表所掌握,采用高低模式对数据进行存放的基本点区别在以存放的字节顺序,大端方式将高位存放于亚地址,小端方式以高位存放于大地址。采用大端方式展开数据存放符合人类的正常思维,而用小端方式开展多少存放利于计算机处理。到目前为止,采用大端或者小端进行数量存放,其孰优孰劣也从没结论。

有些处理器系统运用了小端方式展开数据存放,如Intel的驰骋。有的处理器系统采取了多方方式开展多少存放,如IBM半导体及Freescale的PowerPC处理器。不仅对于电脑,一些外设的统筹被呢在在以大端或者小端进行数据存放的抉择。

所以于一个电脑系统受,有或存在多方面和小端模式还要存在的景。这无异场景吧系统的软硬件设计带来了未聊之难为,这要求系统规划工程师,必须深入理解大端和小端模式的差别。大端与小端模式之别体现于一个计算机的寄存器,指令集,系统总线等次第层次中。

【用函数判断系是Big Endian还是Little Endian】

//如果字节序为big-endian,返回true; //反之为   little-endian,返回false

bool IsBig_Endian() {     unsigned short test = 0x1234;     if(*(
(unsigned char*) &test ) == 0x12)        return TRUE;    else       
return FALSE;

 

}//IsBig_Endian()

 

附:

  大小端的分度值是
byte,即每一个byte都是按部就班正规顺序,但是byte组装成一个int 或者是
long等时每个byte的陈设位置不同


分明,同样一致组数据,存储和表示的各个可以是多如牛毛之,也就是储存和代表格式是密密麻麻底[1]。相同的数量,转换成为二进制之后,在不同之微机存储器中的里边表示为是起分之,这会指向编程产生一定的震慑。本文主要探索大小端存储模式对编程的熏陶及以编程时承诺使用的策略。
1 大小端存储模式概述
计算机中之存储器(内存)由大量底积存最先做。存储老大是存储器的尽小物理组成单位,用来存放一各项二进制数0或1。把这些囤积首届按同的位数(通常是1配节8个之1,
2, 4,
8倍)划分成组,组内所有存储元又读来或写副信息,即为存储单元[2]。每个存储单元都发一个独一无二的号子,叫做单元地址。存储单元是CPU访问存储器的主导单位,CPU通过单元地址访问(读或写)相
应的存储单元。不同之计算机,存储单元地址之编排方式有所不同。如果展开编址的顶小单位是配,称为按字编址,如图1
(a)所示。如果编址的太小单位是字节,则称仍字节编址。因此,
CPU一不良看一个存储单元就可能拜会若干只单身编址的字节,图1(b)为PDP-11机器,一个存储单元存放2个字节,低字节用偶地址,高字节用奇地址,字地址是2底倍数,即其的低字节的地点。图1(c)为IBM-370机器,一个存储单元存放4个字节,字地址是4的整数倍增,即她的高字节的地址(与图1(b)所示情况反而)。图1 存储器的不等编址方式随字节编址是当下存储器编址方式的主流,因为数量处理的太小单位凡字节。从软件角度看,存储器就是一个很可怜的字节数组。通常,CPU和编译器使用不同之格式来编码数据,如不同尺寸的平头和浮点数,从而支持多多少类。程
序中,先定义这些项目的变量,到内存中分配字节空间,当CPU读写这些变量,即看内存时,往往根据数据类型的不比,一次等而读写几独字节。对于多于一个字节的数码(通常也字节长度的2N倍,N=1,
2,
3),在存储器中有三三两两种植存储方,即定义半配、字、双字与字节之间的附和关系的蝇头种植炫耀机制[3]。一栽是数量的低字节部分存放在内存低地址处,高字节部分存放于内存高
地址处,称为小端字节顺序存储法,又如小端存储模式;另一样种是高字节数据存放于小地址处,低字节数据存放于胜地址处,称为大端字节顺序存储法,又如大端存储模。不难看出,图1(b)所示为小端模式,而贪图1(c)即为多边模式。支持多边存储模式要小端存储模式,并无存在技术原因,只是提到到电脑厂商的立场与习惯。
2 存储模式不同而造成的题目
对于多数程序员而言,机器的字节存储顺序是一点一滴不可见的,无论哪一样种存储模式之微机编译出底次序还见面获同之结果。即对同一段源代码,单独在小端机器及编译运行,其结果与独立在多方机器及编译运行的结果一律,尽管与一个数据在大大小小端格式下之内存表示有分,但于应用程序员和用户眼里,参与算术逻辑运算、写副读出的数目却是无别之。不过,有些情况下,字节顺序会成为问题。

  1. 1 UNIX题目———程序可移植性问题
    最早以拿UNIX操作系统的首版本从PDP-11移植到IBM机器上常,数据“UNIX”在16各字长的小端格式的PDP-11上给代表为2独字4个字节,当让移植到多方存储模式的IBM机器及时不时,就见面化为“NUXI”,这吃名UNIX问题。因此,当以不同存储顺序的处理器间进行次移植时,需要特别注意存储模式之熏陶。
  2. 2 阅读、解释、共享二进制数据的题材
    对同一段可执行程序进行反汇编,使用反汇编器阅读机器级二迈入制代码时,在不同存储模式之处理器上会看不同的结果;在解说、共享为第二上制格式存储的多少与用掩码时,不同之存储顺序会得出不同的结果,例如,某32各类小端机器,存储某常量0xE48623A0到某个次进制文件,在多方模式下读出的是0xA02386E4,若将欠多少作为IPv4地址,则存储并行使方便的掩码进行按位与运算也得考虑到囤模式之熏陶。
  3. 3 网络数据传的问题
    当不同存储模式的微机之间通过网络传送二上制数据时,会发高低字节翻转现象,例如,从32号小端机器,发送某常量0x01234567,发送和接收缓冲区内(地址从低及大)的字节顺序为0x67、0x45、0x23、0x01,接收该数据的对方机器也
    32员大端模式,则读出的数值是0x67452301,相比原值,高低字节互换了职务。
    3 编程时可运的策略
    编程时,应考虑该使用是否与存储模式相关,若需以不同存储模式的微处理器之间移植程序、共享数据、网络通信,可以品味以下对策。若是程序移植,则当次中上加如下的预编译条件,针对不同存储模式,分别处理,然后在峰文件被定义当前计算机和编译器支持之蕴藏模式,如:
    #define Little-End #ifdefBig_End   …… #endif #ifdefLittle_End
      …… #endif 若是信息共享,则发出个别种缓解办法:
    (1)以十足存储顺序共享数据,只需要解释一种格式,所以解码简单;
    (2)允许各主机为不同之贮存顺序共享数据,但得标记出啦种模式,无需对数据的本原顺序进行中转,所以编码容易,当发送方编码和接收方解码采用同样种存储模式时,无需变换字节顺序,可以增强通信效率。

倘网络通信,可以参考并遵照TCP/IP协议定义的正统的大网字节顺序,由发送方处理器先在那个里面以发送的多少易成纱正式,而接收方处理器再以网络正式转换为它们的其中表示。Berkeley应用程序接口定义了平效转换函数,如:函数htonl和htons分别用32位长整型和16号短整型数值从主机字节顺序转化成为网字节顺序;而函数ntohl和ntohs则用网络字节顺序转化为主
机字节顺序。 4 案例解析
ZLG/IP可运行于多方机器,也只是运行为小端机器,涉及程序的可移植性;
ZLG/IP是嵌入式网络通信协议,必然涉及多独主机里的数码共享与网传输;由大小端存储模式不同而招致的题材,ZLG/IP都见面遇上,它是哪些缓解之吧?限于篇幅,这里只摘录IP.
c中IP报头的殡葬、校验、接收函数的局部源代码。存储模式不同不会见潜移默化IP报头的字节变量成员的读写,也不见面潜移默化IP地址之读写(IP地址按大端顺序以字节数组存储),所以,只选IP报头的16位半字变量成员作案例分析。

  1. 1 发送函数
    原版本定义了一个片段字节数组,无论以哪种存储模式下编译运行,均用半字变量拆分成两独字节,高字节写副小地址,低字节写副强地址,即直接以大端顺序写副字节数组,保证发送和接收缓冲区的内存表示的一致性,即网络传输的IP报头的一致性。
    eip e_ip; uint8 IpHeadUint8[20]; …… e_ip. TotalLen=(*TxdData).
    length+20; IpHeadUint8[2]=(e_ip.TotalLen&0xff00)>>8;
    IpHeadUint8[3]=e_ip.TotalLen&0x00f;f ……
    e_ip.Crc=CreateIpHeadCrc(IpHeadUint8); IpHeadUint8[10]=(e_ip.
    Crc&0xff00)>>8; IpHeadUint8[11]=e_ip.Crc&0x00f;f ……
    TxdIpData.DAPTR=IpHeadUnit8; Send_Ip_To_LLC (&TxdIpData, e_ip.
    DestId, num); 笔者的做法是下并用体类型union
    ip-rc实现,可以省局部字节数组空间,大小端情况分别对待,大端模式下,直接写副原值;小端模式下,先勾勒副原值,再针对该变量成员作高低字节转换,即最终写副的价值不同(与原值字节顺序相反),内存代表也变的一致了(原值的多方顺序)。
      union ip_rc {eip e_ip;        struct {uint16 wordbuf[10];
    } words; };   union ip_rc IpHead;   ……   IpHead. e_ip.
    TotalLen=(*TxdData). length+20; #ifdefLittle_End   IpHead. e_ip.
    TotalLen=swap_ int16( IpHead e_ ip. To- talLen); #endif   ……
      IpHead. e_ip. Crc=CreateIpHeadCrc_1 ( IpHead. words. wordbuf);
    #ifdefLittle_End   IpHead. e_ip. Crc=swap_int16(IpHead. e_ip.
    Crc); #endif   ……   TxdIpData. DAPTR=(uint8* ) & IpHead. e_ip;
      Send_Ip_To_LLC (& TxdIpData, IpHead. e_ip. DestId, num); 4.
    2 IP报头校验和计算函数
    不论哪种存储模式下,为了保证校验和结果的惟一性,原版本对传递过来的8个字节数组(内存表示默认为大端顺序)强制按半配16位大端顺序读来与求和。
    union w Crc: //类型union w在ip. h中定义,存储模式不同,定义为不同 Crc.
    dwords=0; for ( i=0; i<10; i++) Crc. dwords=Crc. dwords+ ((uint 32)
    Ip[2* i]<<8) + (uint32)Ip[2* i+1];
    笔者实现的校验和计算函数更简明又通用,形参16各类半字数组是确定无转移的大举字节顺序,大端模式下,读出的IpH[
    i]就算是原值,直接抬高即可;小端模式下,读来IpH[
    i]晚必变换高低字节,才是当年形容副的原值,然后又添加。 uint32 temp=0; for
    ( i=0; i<10, i++) { #ifdefBig_End  temp=temp+(uint32) IpH[ i];
    #endif #ifdefLittle_End  temp=temp + (uint32) (swap_int16 (IpH[
    i])); #endif } 4. 3 接收函数
    原版本被,传递过来的IP报头是多方面顺序字节数组中的数额,在多边模式下念来时,就是不错的原值,在小端环境下编译运行时,内存表示是多方面顺序,却照小端顺序读来,结果值和原值高低字节顺序相反,因此,要将读出的结果开展高低字节互换,才能够取正确的原值。
    #ifdefBig_End  PackedLength=((eip* )RecData)->TotalLen; #endif
    #ifdefLittle_End  PackedLength=((eip* )RecData)->TotalLen;
     Ltemp=PackedLength&0x00f;f
     PackedLength=(PackedLength&0xff00)>>8;
     PackedLength=PackedLength+(Ltemp<<8); #endif
    而笔者精减了代码,读操作是无论哪种存储模式下都存在的,可以统一出,之后对小端模式之特殊状况,进行高低字节转换,变扭原值。
     PackedLength=((eip* )RecData)->TotalLen; #ifdefLittle_End
     PackedLength=swap_int16 (PackedLength)); #endif 通过看源码得知,
    ZLG/IP约定:无论以哪种存储模式的机器上编译运行,
    IP报头在发送和接收缓冲区中之字节顺序表示为多边字节顺序。这样的约定来星星点点只好处:
    (1)保证了网通信的数据包报头的字节顺序一致,形成网络字节顺序的正规,当于通信双方的不同之存储模式下念来时,由终端计算机体系协调转换;
    (2)方便了报头校验和底盘算,不论大小端存储模式,一致按大端
    顺序读来,保证了校验和之惟一性。 5 结语
    由上述分析可见,本文所描写代码可以节约局部数组空间,代码量更不见,函数模块更通用。因此,可以更进一步精简ZLG/IP中ICP报头、UDP报头的出殡、检验、接收源代码。

好家伙时候如果拓展高低端字节序的更换?  

short 或者 long的多寡在进行通信的时节太好养成:
 1、发送的时刻用:htons(l)  2、接受之早晚以:ntohs(l)
 而不要理睬两止的通信是否用如此做~~
 当然矣貌似我都毫不int型的多寡通信,从来都是字符串通信,发送方利用sprintf组织,接收方利用atoi进行更换~~