字节序问题之大小端模式讲解
一、什么是大小端模式
大端模式(Big-Endian):指的是数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中.
小端模式(Little-Endian):指的是数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。
上述的描述准确的说明了大小端模式的数据排列方式,但是还不够直观,下面我们来举个例子:
如:数字 0x12345678 在内存中的表示形式为:
大端模式:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78
小端模式:
低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
大小端模式的优劣势比较
大端模式:强制转换数据不需要调整字节内容。
小端模式:符号位的判断直接看第一个字节,容易判断正负。
基本上各自的优点就是对方的缺点,采用大端存储模式存储数据更符合人类的思维,采用小端存储数据更方便计算机进行数据处理。
开发的时候必须要考虑大小端模式问题,若不考虑这些就收发数据会发生问题,因为保存顺序的不同意味着对接收数据的解析顺序也不同。
二、为什么有大小端模式
因为在计算系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。对于位数大于8位的处理器,例如目前大部分都32位或者64位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
三、常见字节序的模式
1、常见的CPU的字节序
大端模式: PowerPC、IBM、Sun;
小端模式 : x86、DEC;
ARM既可以工作在大端模式,也可以工作在小端模式。
2、常见文件的字节序
大端模式:Adobe PS、JPEG、MacPaint;
小端模式:BMP、GIF、RTF;
另外,Java和所有的网络通讯协议都是使用Big-Endian的编码。在对普通文件进行处理也需要考虑端模式问题。在大端模式的处理器下对文件的32,16位读写操作所得到的结果与小端模式的处理器不同。单纯从软件的角度理解上远远不能真正理解大小端模式的区别。事实上,真正的理解大小端模式的区别,必须要从系统的角度,从指令集,寄存器和数据总线上深入理解,大小端模式的区别。从实际应用的角度说,采用小端模式的处理器需要在软件中处理端模式的转换,因为采用小端模式的处理器在与小端外设互连时,不需要任何转换。而采用大端模式的处理器需要在硬件设计时处理端模式的转换。大端模式处理器需要在寄存器,指令集,数据总线及数据总线与小端外设的连接等等多个方面进行处理,以解决与小端外设连接时的端模式转换问题。在寄存器和数据总线的位序定义上,基于大小端模式的处理器有所不同。
四、如何判断CPU的字节序
下面这段代码可以用来测试一下你的编译器是大端模式还是小端模式:
short int x; char x0,x1; x=0x1122; x0=((char*)&x)[0]; //低地址单元 x1=((char*)&x)[1]; //高地址单元
五、如何进行大小端转换
这里我们使用C语言大小端转化调用库函数:
在网络传输中,一般要求是大端,而intel处理器是小端,network to host理解为大端转小端,而host to network 理解为小端转大端,本质上大端小端的转化算法是一致的,没有区别。
示例代码如下:
#include<stdio.h> #include<Winsock2.h> #pragma comment(lib,"ws2_32.lib") int main(void) { unsigned int ultest=0x12345678; unsigned short ustest=0x1234; printf("ultest小端:%x\n",ultest); printf("ustest小端:%x\n",ustest); ultest=htonl(ultest);/*unsigned int 大小端转化*/ ustest=htons(ustest);/*unsigned short 大小端转化*/ printf("ultest大端:%x\n",ultest); printf("ustest大端:%x\n",ustest); while(1); }