1. 历史故事
《格列佛游记》
2. 概述
2.1
计算中的大端与小端描述的对象是字节,而不是位。同时,我们规定,计算机中的位编号在最右端从0开始,向左递增,对于四个字节32位来说,地址的编号是这样的:
2.2
让我们先将注意力放到字节上。big-endian(大端)格式存储数字的方式与我们的惯用法相同,最高有效数字从左开始,little-endian(小端)格式从左开始存储最低有效位数字。对于十六进制数字0x0165,大端存储为0165,而小端存储为6501。
x86以及x86-64采用的是小端格式,绝大部分PC以及服务器都是使用这种架构的cpu,制造商包括intel和amd。而ARM处理器可以自行设置大端或者小端。
实验环境:windows 10 + intel cpu + visual studio
我们在vs下写下如下代码
int main()
{
int a = 0x00100110;
a = 0x01100110;
return 0;
}
在a = 0x1100110上打断点,然后启动调试器,并打开内存窗口:
这里的a = 0x00 10 01 10,注意这里是16进制,每2个数字占1个字节。可以看到,内存窗口中的地址从上到下递增,变量a的地址从0x000000E3F1FAFC24到0x000000E3F1FAFC27。而最高位地址保存的是我们数据的最高位,最低位地址保存的是数据的高低位,即0x000000E3F1FAFC27保存了数据的高位00,而0x000000E3F1FAFC24保存了数据的低位10。再回来看我们的变量a=0x00 10 01 10。从人类的习惯来看,从高到低是从左到右的,所以00是高字节,10是低字节,从而我们可以得出如下结论:
将最高字节放在最低位地址,这种顺序叫大端格式;
将最高字节放在最高位地址,这种顺序叫小端格式;
现在回过头来看上面的0x01 65,01是高字节,而65是低字节。当我们从内存的低地址看到高地址时,小端看到的就是6501,而大端看到的就会是0165。
2.3
现在让我们来思考另一个问题,我们知道一个int占4个字节。那么要怎么取出一个int中的高16位与低16位呢?
首先,我们说一个int中的高16位与低16位是对人类而言的,如对一个整数b = 0x 12 34 56 78,12 34就是高16位,56 78就是低16位。对小端而言,取高16位就是进行右移16位的运算,即 b >> 16,取低16位就是左移16位,即b << 16。这是与人的习惯相符的。而如果使用大端存储,那么应该进行相反的操作。
3. 实战
3.1 windows上转换大小端
相关函数名:
htonl、htons、ntohl、ntohs
3.2 linux上转换大小端
linux上也包含相关的htonl、htons、ntohl、ntohs,同时还提供了额外的头文件endian.h,相关函数名为htobe64, htobe32等。