字节序:
字节序,又称端序,尾序,英文:Endianness。在计算机科学领域中,字节序是指存放多字节数据的字节(byte)的顺序,典型的情况是整数在内存中的存放方式和网络传输的传输顺序。Endianness有时候也可以用指位序(bit)。
一般而言,字节序指示了一个UCS-2字符的哪个字节存储在低地址。如果LSByte在MSByte的前面,即LSB为低地址,则该字节序是小端序;反之则是大端序。在网络编程中,字节序是一个必须被考虑的因素,因为不同的处理器体系可能采用不同的字节序。在多平台的代码编程中,字节序可能会导致难以察觉的bug。
引言:
说到程序间的通信,说到底便是发送数据流。我们一般把字节(byte)看作是数据的最小单位。当然,其实一个字节中还包含8个比特(bit)──有时候我奇怪为什么很多朋友会不知道bit或是它和byte的关系。当我们拿到一系列byte的时候,它本身其实是没有意义的,有意义的只是“识别字节的方式”。例如,同样4个字节的数据,我们可以把它看作是1个32位整数、2个Unicode、或者字符4个ASCII字符。
同样我们知道,在一个32位的CPU中“字长”为32个bit,也就是4个byte。在这样的CPU中,总是以4字节对齐的方式来读取或写入内存,那么同样这4个字节的数据是以什么顺序保存在内存中的呢?例如,现在我们要向内存地址为a的地方写入数据0x0A0B0C0D,那么这4个字节分别落在哪个地址的内存上呢?这就涉及到字节序的问题了。
一.基本字节序
对于单一的字节(a byte),大部分处理器以相同的顺序处理位元(bit),因此单字节的存放方法和传输方式一般相同,无需考虑字节序问题。
对于多字节数据,如整数(32位机中一般占4字节),在不同的处理器的存放方式主要有两种,以内存中0x0A0B0C0D的存放方式为例,分别有以下几种方式:
1. 大端序(英:big-endian)或称大尾序。(与书写方式一致,很直观)
数据以8bit为单位:
地址增长方向 →
... 0x0A 0x0B 0x0C 0x0D ...
示例中,最高有效位(MSB, Most Significant Byte)是0x0A 存储在最低的内存地址处。下一个字节0x0B存在后面的地址处。正类似于十六进制字节从左到右的阅读顺序。
数据以16bit为单位:
地址增长方向 →
... 0x0A0B 0x0C0D ...
最高的16bit单元0x0A0B存储在低位。如何判断当前字节序类型
2. 小端序(英:little-endian)或称小尾序。(与表达方式一致,高位数字放高位地址,低位数字放低位地址)
数据以8bit为单位:
地址增长方向 →
... 0x0D 0x0C 0x0B 0x0A ...
最低有效位(LSB,Least Significant Byte)是0x0D 存储在最低的内存地址处。后面字节依次存在后面的地址处。
数据以16bit为单位:
地址增长方向 →
... 0x0C0D 0x0A0B ...
最低的16bit单元0x0C0D存储在低
3.混合序
混合序(英:middle-endian)具有更复杂的顺序。以PDP-11为例,0x0A0B0C0D被存储为:
32bit在PDP-11的存储方式
地址增长方向 →
... 0x0B 0x0A 0x0D 0x0C ...
可以看作最高的16bit位和低位以大端序存储,但16bit内部以小端存储。
二.如何判断当前字节序类型
处理器体系
x86,MOS Technology 6502,Z80,VAX,PDP-11等处理器为Little endian。
Motorola 6800,Motorola 68000,PowerPC 970,System/370,SPARC(除V9外)等处理器为Big endian
ARM, PowerPC (除PowerPC 970外), DEC Alpha, SPARC V9, MIPS, PA-RISC and IA64的字节序是可配置的。
我的系统(unbuntu)在vim /usr/include/bits/endian.h定义了
#define __BYTE_ORDER __LITTLE_ENDIAN
写个小程序测试下:
int main()
{int a = 0x6465;
char* p1 = &a;
char* p2 = p1+1;
printf("%x,%c,%c,%c,%x,%x\n",a,a,*p1,*p2,*p1,*p2);
*(char*)&a = 0x66;
printf("%x,%c,%c,%c,%x,%x\n",a,a,*p1,*p2,*p1,*p2);
return 0;
}
结果输出:
可以看到存储a的地址单元(共4个字节)第一个被修改了,为小端序。
关于内存字节的存放相关还有struct结构体,有空再贴一篇上来。