计算机网络 | 网络字节序 / 字节序类型验证 / 字节序转换

注:本文为 “大小端字节序” 相关合辑。

英文引文,机翻未校。
中文引文,略作重排,未整理去重。
两篇前介一样,看应用。第二篇略作重编。
如有内容异常,请看原文。


大小端及网络字节序

z_ryan 于 2018-01-22 23:58:21 发布

大端模式、小端模式

“大端” 和” 小端” 表示多字节值的哪一端存储在该值的起始地址处:小端存储在起始地址处,即小端字节序;大端存储在起始地址处,即大端字节序;具体的说:

① 大端字节序(Big Endian):最高有效位存于最低内存地址处,最低有效位存于最高内存处;

② 小端字节序(Little Endian):最高有效位存于最高内存地址,最低有效位存于最低内存处。

如下图:当以不同的存储方式,存储数据为 0x12345678 时:

在这里插入图片描述

判断字节序

可以通过下面的小程序测试自己的机器是大端字节序还是小端字节序

#include <stdio.h>
union
{
    char ch;
    int i;
}un;
int main(void)
{
    un.i = 0x12345678;
    if(un.ch == 0x12)
    {
        printf("big endian\n");
    }
    else
    {
        printf("small endain\n");
    }
    return 0;

在测试程序中,使用联合体的原因是:union 型数据所占的空间等于其最大的成员所占的空间。对 union 型成员的存取都是相对于该联合体基地址的偏移量为 0 处开始,也就是联合体的访问不论对哪个变量的存取都是从 union 的首地址开始的。通过检测第一个字节存放的数据即可得出结果。

网络字节序

网络上传输的数据都是字节流,对于一个多字节数值,在进行网络传输的时候,先传递哪个字节?也就是说,当接收端收到第一个字节的时候,它将这个字节作为高位字节还是低位字节处理,是一个比较有意义的问题;

UDP/TCP/IP 协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节。

也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节 (即:高位字节存放在低地址处); 由此可见,多字节数值在发送之前,在内存中因该是以大端法存放的;所以说,网络字节序是大端字节序。

在实际中,当在两个存储方式不同的主机上传输时,需要借助字节序转换函数。

字节序转换函数

 #include <arpa/inet.h>

//将主机字节序转换为网络字节序
 unit32_t htonl (unit32_t hostlong);
 unit16_t htons (unit16_t hostshort);
 //将网络字节序转换为主机字节序
 unit32_t ntohl (unit32_t netlong);
 unit16_t ntohs (unit16_t netshort);

 说明:h -----host;n----network ;s------short;l----long

例如:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    unsigned int x = 0x12345678;
    unsigned char *p = (unsigned char *)&x;
    printf("%0x_%0x_%0x_%0x\n",p[0],p[1],p[2],p[3]);

    unsigned int y = htonl(x);
    p = (unsigned char*)&y;
    printf("%0x_%0x_%0x_%0x\n",p[0],p[1],p[2],p[3]);

    return 0;
}

运行结果:

这里写图片描述


网络字节序(大端字节序(Big Endian)/ 小端字节序(Little Endian)

JMW1407 于 2020-09-26 08:23:28 发布

1、网络字节序 (Network Byte Order)和本机转换

1、大端、小端字节序

“大端” 和” 小端” 表示多字节值的哪一端存储在该值的起始地址处:小端存储在起始地址处,即小端字节序;大端存储在起始地址处,即大端字节序;具体的说:

① 大端字节序(Big Endian): 最高有效位存于最低内存地址处,最低有效位存于最高内存地址处;

② 小端字节序(Little Endian):最高有效位存于最高内存地址,最低有效位存于最低内存地址处。

“高位数据"和"低位数据”

通常指的是数据在存储或传输时的位置或顺序。

在处理二进制数据、字节序、位操作以及数据存储时非常常见,用于描述数据的物理存储方式或传输顺序。

  • 高位数据:指的是数据的高阶位或高字节,通常存储在数据块的起始位置或最高有效位(Most Significant Bit,MSB)。在多字节数据中,高位数据对应于数据的较高位部分。

  • 低位数据:指的是数据的低阶位或低字节,通常存储在数据块的末尾或最低有效位(Least Significant Bit,LSB)。在多字节数据中,低位数据对应于数据的较低位部分。

如下图:当以不同的存储方式,存储数据为 0x12345678 时:

视角 1
在这里插入图片描述

视角 2

在这里插入图片描述
视角 3
在这里插入图片描述

网络字节序:大端字节序

网络上传输的数据都是字节流,对于一个多字节数值,在进行网络传输的时候,先传递哪个字节?
也就是说,当接收端收到第一个字节的时候,它将这个字节作为高位字节还是低位字节处理,是一个比较有意义的问题。

UDP/TCP/IP 协议规定:把接收到的第一个字节当作高位字节看待, 这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节。换句话说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节。

所以,网络字节序就是大端字节序, 有些系统的本机字节序是小端字节序,有些则是大端字节序,为了保证传送顺序的一致性, 网际协议使用大端字节序来传送数据

验证字节序类型

/* 确定你的电脑是大端字节序还是小端字节序 */
#include <stdio.h>

int check1()
{
	int i = 1; //1在内存中的表示: 0x00000001
	char *pi = (char *)&i; //将int型的地址强制转换为char型
	return *pi == 0; //如果读取到的第一个字节为1,则为小端法,为0,则为大端法
}

int main()
{
	if (check1() == 1)
		printf("big\n");
	else
		printf("little\n");

	return 0;
}

第二种方法,用联合结构解决,其本质差异不大

/* 确定你的电脑是大端字节序还是小端字节序 */
#include <stdio.h>

int check2()
{
	union test {
		char ch;
		int i;
	}test0;
	test0.i = 1;
	return test0.ch == 0;
}
int main()
{
	if (check1() == 1)
		printf("big\n");
	else
		printf("little\n");

	return 0;
}

因为联合结构中的变量共用一块存储空间,所以 ch 和 i 拥有同一个地址:
在这里插入图片描述

对本例中的联合结构,对它求 sizeof(test0),会发现它的大小为 4,取了 int 的大小。

关于 union,它里边的变量共用一块存储空间,但是它的大小并不总是其中最大的变量所占的空间,还需要考虑对齐!

比如:

union test1 {

char[5];

int i;

}

它的大小就是 8 了!

2、字节序转换函数

/* 字节序转换函数 */
 #include <arpa/inet.h>

//将主机字节序转换为网络字节序
 uint32_t htonl (uint32_t hostlong);
 uint16_t htons (uint16_t hostshort);
 //将网络字节序转换为主机字节序
 uint32_t ntohl (uint32_t netlong);
 uint16_t ntohs (uint16_t netshort);

 说明:h -----host;n----network ;s------short;l----longhtons()--"Host to Network Short"
htonl()--"Host to Network Long"
ntohs()--"Network to Host Short"
ntohl()--"Network to Host Long"

数据结构 struct sockaddr_in 中字节序

为什么在数据结构 struct sockaddr_in 中, sin_addr 和 sin_port 需要转换为网络字节顺序,而 sin_family 不需要呢?

解析sin_addrsin_port 分别封装在包的 IPUDP 层。因此,它们必须是网络字节顺序。但是 sin_family 域只是被内核 (kernel) 使用来决定在数据结构中包含什么类型的地址,所以它必须是本机字节顺序。同时, sin_family 没有发送到网络上,它们可以是本机字节顺序

IP 地址的三种表示格式及在开发中的应用

IP 地址的三种表示格式

  • 1)点分十进制格式
  • 2)主机字节序格式
  • 3)网络字节序格式

用 IP 地址127.0.0.1为例:

第一步 127 . 0 . 0 . 1 把 IP 地址每一部分转换为 8 位的二进制数。(点分十进制)

第二步 01111111 00000000 00000000 00000001 = 2130706433 (主机字节序)

然后把上面的四部分二进制数从右往左按部分重新排列,那就变为:

第三步 00000001 00000000 00000000 01111111 = 16777343 (网络字节序)

IP 地址转换函数

1、函数inet_addr(),将 IP 地址从 点数格式转换成无符号长整型。使用方法如下:

函数原型

in_addr_t inet_addr(const char *cp);

转换网络主机地址(点分十进制)为网络字节序二进制值,

  • cp 代表点分十进制的 IP 地址,如 1.2.3.4
  • 如果参数 char *cp 无效则返回 - 1 (INADDR_NONE),
  • 此函数有个缺点:在处理地址为 255.255.255.255 时也返回 - 1,虽然它是一个有效地址,但 inet_addr () 无法处理这个地址。

使用

ina.sin_addr.s_addr = inet_addr("132.241.5.10");

现在可以将 IP 地址转换成长整型了。有没有其相反的方法可以将一个 in_addr 结构体输出成点数格式?

2、函数 inet_ntoa()(“ntoa"的含义是"network to ascii”),就像这样:

函数原型

char* inet_ntoa(struct in_addr in);

参数:

  • in 代码 in_addr 的结构体,其结构体如下:
struct in_addr 
{
    union 
    {
        struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
        struct { USHORT s_w1,s_w2; } S_un_w;
        ULONG S_addr;
    } S_un;
};

使用

SOCKADDR_IN sock;
sock.sin_family = AF_INET;
//将字符串转换为 in_addr 类型
sock.sin_addr.S_un.S_addr =  inet_addr("192.168.1.111");
sock.sin_port = htons(5000);
 
//将 in_addr 类型转换为字符串
printf("inet_ntoa ip = %s\n",inet_ntoa(sock.sin_addr));


结果输出:
inet_ntoa ip = 192.168.1.111

注意
inet_ntoa()将结构体in_addr作为一个参数,不是长整形。需要注意的是它返回的是一个指向一个字符的指针,一个由inet_ntoa() 控制的静态的固定的指针,所以每次调用 inet_ntoa(),它将覆盖上次调用时所得的 IP 地址
例如:

char *a1, *a2;
……
a1 = inet_ntoa(ina1.sin_addr); /* 这是198.92.129.1 */
a2 = inet_ntoa(ina2.sin_addr); /* 这是132.241.5.10 */
printf("address 1: %s\n",a1);
printf("address 2: %s\n",a2);

输出如下:

address 1: 132.241.5.10
address 2: 132.241.5.10

字节序探析:大端与小端的比较

作者: 阮一峰

日期: 2022 年 6 月 3 日

谈一个重要的计算机概念,大家可能都听说过它,但是很少深究,那就是字节序(Endianness)。

img

一、概念

字节序指的是,多字节数据的内存排列顺序。这样说比较抽象,使用图形解释就很好懂。

内存好比一排房间,每个字节是一间房。每间房都有门牌号(内存地址),从 0 号开始,然后是 1 号、2 号…

img

0 号字节的地址小,称为低位内存;3 号字节的地址大,称为高位内存。

现在有一个数值 abcd 要放进这些房间,每个房间放一个数字,那么有两种放法。

第一种放法是,第一位 a 放在低位地址(0 号),最后一位 d 放在高位地址(3 号)。

img

这种排列称为 “大端序”(big-endian,简称 BE),即大头在前,因为 aabcd 的大头(最重要的数字)。

第二种放法是,第一位 a 放在高位地址(3 号地址),最后一位 d 放在低位地址(0 号地址)。

img

这种排列称为 “小端序”(little-endian,简称 LE),即小头 d 在前。

大端序和小端序合称字节序,这两个名字来自 18 世纪的英国小说《格列佛游记》。某国分成两派,一派认为鸡蛋应该从大头吃起,称为 “大端派”;另一派认为,鸡蛋应该从小头吃起,称为 “小端派”。两派相执不下,谁也无法说服谁,最后甚至为此交战。

img

二、可读性

对于人类来说,不同字节序的可读性是不一样的。大部分国家的阅读习惯是从左到右阅读。

img

大端序的最高位在左边,最低位在右边,符合阅读习惯。所以,对于这些国家的人来说,从左到右的大端序的可读性更好。

但是现实中,从右到左的小端序虽然可读性差,但应用更广泛,x86 和 ARM 这两种 CPU 架构都采用小端序,这是为什么?

或者换一种问法,两种不同的字节序为什么会并存,统一规定只使用一种,难道不是更方便吗?

原因是它们有各自的适用场景,某些场景大端序有优势,另一些场景小端序有优势,下面就逐一分析。

三、检查奇偶性

小端序优势最明显的,大概就是检查奇偶性,即通过查看个位数,确定某个数字是奇数还是偶数。

img

123456 为例,大端序从左到右排列,计算机必须一直读到最后一位的个位数 6,才能确定这是偶数。

小端序是从右到左排列,个位数在第一位。所以,只要读取第一位,就能确定它是偶数。

四、检查正负号

一个类似的场景是检查正负号,确定一个数是正数还是负数。

img

大端序的符号位在左边第一位,小端序的符号位在右边最后一位。所以,大端序有优势,只看第一位就能知道是不是负数。

五、比较大小

下一个操作是比较大小。现在有三个数字,需要比较大小:43662576,594,2。

img

上图是大端序排列,因为是从左到右排列,所以三个数字在右边个位数对齐。比较大小时,计算机就不得不读取每一个数的所有位,直到个位数,再进行比较。

如果改成小端序,就是下面的排列方式。

img

小端序是从右到左,所以三个数字在第一位对齐。计算机就不需要读取所有位,哪个数字先读不到下一位,就是最小的。比如,2 这个数字就没有第二位,所以读到第二位时,就知道它是最小的。

所以,比较大小时,小端序有优势。

六、乘法

接下来,再看乘法操作。

乘法是逐位相乘,每一轮乘法都要向前进位。

img

上图是大端序的 24165 乘以 3841。大端序的乘法是向左进位,也就是向左边扩展,必须等到每一轮的结果都出来(上例是四轮),再相加统一写入内存。

如果改成小端序的乘法,就不需要等待下一轮的结果,每一轮都可以直接写入内存。

img

上图是小端序的 24165 乘以 3841。小端序的乘法是向右进位,也就是向右边扩展,左边的边界不变。每一轮结果写入内存后,就不需要移动,后面有变化只需要改动对应的位就行了。

因此,小端序的乘法有明显优势。

七、任意精度整数

上一个例子的从低位开始计算的特性,对于任意精度整数特别有用。任意精度整数又称大整数,可以存放任意大小的整数。

它的内部实现是把整数分成一个个较小的单位,通常是 uint32(无符号 32 位整数)或 uint64(无符号 64 位整数),按顺序组合在一起。

img

如果是大端序,第一个 u64 就是这个整数最大的部分。运算时,一旦这个数发生变化,需要进位,后面的所有位都必须移动和改写。小端序发生进位时,往往就不需要所有位移动。

小端序的另一个好处是,如果逐字节的运算从个位数开始(比如乘法和加法),可以从左到右依次运算一个个 u64,算完上一个再读取下一个。大端序就不行,必须读取整个数以后再进行运算。

八、更改类型

最后一个例子是,C 语言有一种 cast 操作,可以强制改变变量的数据类型,比如把 32 位整数强行改变为 16 位整数。

img

上图中,32 位整数 0x00000001 更改为 16 位整数 0x0001,大端序是截去前面两个字节,这时指向这个地址的指针必须向后移动两个字节。

小端序就没有这个问题,截去的是后面两个字节,第一位的地址是不变的,所以指针不需要移动。

九、总结

综上所述,大端序和小端序各自的优势如下。

如果需要逐位运算,或者需要到从个位数开始运算,都是小端序占优势。反之,如果运算只涉及到高位,或者数据的可读性比较重要,则是大端序占优势。


网络序和主机序、大端和小端问题详解

杜杜杜f于 2021-04-13 08:58:48 发布

1. 什么是网络序、主机序、大端、小端

大端、小端均为数据存储方式,网络字节序采用大端方式,多数计算机采用小端模式。

  • 大端
    高位字节存放在低地址,低位字节存放在高地址。数据字节位随内存地址的增长而减小。

    大端字节序在网络抓包时的呈现形式符合人类的阅读习惯

  • 小端
    高位字节存放在高地址,低位字节存放在低地址。数据字节位随内存地址的增长而增长。

    小端字节序虽不符合人类的直观阅读习惯,但有利于计算机硬件进行低位计算

2. 为什么要区分大端与小端

  • 在 CPU 硬件设计中,为便于计算,通常从低位开始处理,小端存储方式更适配这一特点。
  • 小端方式不利于人类阅读,而网络抓包采用大端方式可解决这一问题。

3. 什么时候需要转换大端与小端

  • 内存存储以 Byte 为单位,对于单 Byte 变量的存储,不存在大端与小端的区分。
  • C 语言中内置的 int、double、short 等多字节类型,存储时存在两种方式(即上述大端(低地址存高位)与小端(低地址存低位))。当此类变量由 host1 发送至 host2 时,需进行大小端转换:host1 发送至网络时执行主机序到网络序的转换(hton),host2 接收时执行网络序到主机序的转换。

4. 实际验证

  • 主机序、小端验证
    以 UINT16 短整型变量存储以太帧类型,以 0 x 0800 0\text{x}0800 0x0800 为例,通过 gdb 查看实际内存存储。

(gdb) l
1 #include<stdio.h>
2
3 int main()
4 {
5 unsigned short eth_type=0x0800;
6
7 return 1;
8 }

(gdb) b main
Breakpoint 1 at 0x400478: file gdb_test.c, line 5.
(gdb) r
Starting program: /home/fdu/Desktop/gdb_test

Breakpoint 1, main () at gdb_test.c:5
5 unsigned short eth_type=0x0800;
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.212.el6.x86_64
(gdb) n
7 return 1;
(gdb) p/x eth_type
$1 = 0x800
(gdb) x/2xb eth_type
0x7fffffffe50e: 0x00 0x08

可以发现 0 x 0800 0\text{x}0800 0x0800 在计算机中实际以小端方式存储(即 0 x 0008 0\text{x}0008 0x0008),但在代码层面无需感知这一存储细节。

  • 网络序、大端验证
    通过抓包可观察到,网络字节流中 0 x 0800 0\text{x}0800 0x0800 以大端方式存储(即 0 x 0800 0\text{x}0800 0x0800)。

img

5. 若场景为 host1 发送 TCP 报文至 host2,而 host2 接收时未进行网络序转主机序,会出现什么情况?

报文字节流为网络字节序,host2 接收后未执行主机序转换(仍以大端方式存储)。此时若以小端方式解析报文,解析结果会出现错误。以以太帧类型为例:

uint8_t *raw_buf = m_buf; // 指向报文字节流,未进行大小端转换

uint16_t eth_type = *(uint16_t *)(raw_buf + 12); // eth_type 存储报文类型,未进行大小端转换

以太帧类型的网络字节序为大端 0 x 0800 0\text{x}0800 0x0800

当数据原封不动到达主机端后,通过强制类型转换获取的 eth_type,其内存存储仍为 0 x 08   0 x 00 0\text{x}08\ 0\text{x}00 0x08 0x00

(gdb) x/2xb eth_type

0x7fffffffe50e: 0x08 0x00

但需注意,此时 host2 会以小端方式解析该变量,即内存地址 0 x 7 f f f f f f f e 50 e 0\text{x}7fffffffe50e 0x7fffffffe50e 处的 0 x 08   0 x 00 0\text{x}08\ 0\text{x}00 0x08 0x00 会被解析为 0 x 0008 0\text{x}0008 0x0008

(gdb) p/x eth_type

$3 = 0x8

6. 总结

  • 大端和小端仅是两种不同的存储方式,只要解析方式与存储方式一致,即可保证解析结果的正确性。例如,host1 采用小端存储,host2 采用大端存储,但两台计算机中运行的 C 程序里,变量 eth_type 始终为 0 x 0800 0\text{x}0800 0x0800 这类具有明确含义的协议号,因此开发者无需过度关注大小端细节。
  • 若不进行大小端转换,会导致原本以大端模式存储的数据被误以小端存储方式解读,必然引发错误。

理解大端与小端字节序——原理、实践与网络编程

誰能久伴不乏于 2025-06-09 12:00:16 发布

前言

在 C 语言、操作系统及网络通信的学习过程中,字节序(Endianess)是一个易使初学者乃至有经验的开发者产生困惑的话题。什么是大端、小端?为何网络通信均采用大端?本机的字节序是什么?数据在内存中究竟如何存储?这些问题贯穿底层开发的各个方面。若你对这些问题存在疑惑,本文将带你深入理解大端与小端的本质、存储原理、在网络中的应用及实际编程细节,并结合图示建立直观认知。

1. 字节序的基本概念

1.1 什么是字节序?

字节序指多字节数据类型(如 intfloatstruct 等)在内存中不同字节的排列顺序。最常见的两种字节序为:

  • 大端字节序(Big Endian):高字节存放在低地址,低字节存放在高地址。
  • 小端字节序(Little Endian):低字节存放在低地址,高字节存放在高地址。

高字节指数值上“权重最大”的字节,例如对于 32 位整数 0 x 12345678 0\text{x}12345678 0x12345678 0 x 12 0\text{x}12 0x12 为高字节, 0 x 78 0\text{x}78 0x78 为低字节。

1.2 举例说明

uint32_t num = 0x12345678; 为例:

  • 大端序(Big Endian) 内存排列(地址从低到高):

    0x12 0x34 0x56 0x78
    
  • 小端序(Little Endian) 内存排列(地址从低到高):

    0x78 0x56 0x34 0x12
    

地址顺序从左到右递增。即大端为“高位在前”,小端为“低位在前”。

2. 字节序的本质——存储顺序

无论数据如何变化,字节序的本质区别仅在于存储的先后顺序不同

  • 小端:先存储低字节,再存储高字节。
  • 大端:先存储高字节,再存储低字节。

这导致在不同字节序的机器上直接打印多字节整型变量时,结果会有所不同,但单个字节的数据本身始终正确。

3. 网络字节序与主机字节序

3.1 网络字节序

网络字节序(Network Byte Order) 被标准规定为大端字节序。这意味着:

  • 无论是大端主机还是小端主机,通过网络通信时,传输的多字节数据均需先转换为大端字节序。
  • 此举旨在使所有设备“采用统一格式”,避免因字节序差异导致数据解释混乱。

3.2 主机字节序

主机字节序指本地计算机实际采用的字节排列方式。绝大多数现代桌面设备、服务器、移动设备(如 x86、x64、ARM 架构)均采用小端字节序,部分嵌入式设备或老式服务器则采用大端字节序。

4. 实践:IP 地址与字节序

假设存在 IP 地址 2.3.4.5,通过 inet_pton 转换为网络字节序的 4 字节数据( 0 x 02 0\text{x}02 0x02 0 x 03 0\text{x}03 0x03 0 x 04 0\text{x}04 0x04 0 x 05 0\text{x}05 0x05):

  • 在大端机器上,内存排列为 02 03 04 05
  • 在小端机器上,内存排列为 05 04 03 02

若直接以整型打印,结果会因主机字节序不同而存在差异:

  • 大端: 0 x 02030405 0\text{x}02030405 0x02030405
  • 小端: 0 x 05040302 0\text{x}05040302 0x05040302

但无论何种情况,单个字节的内容始终保持不变!

5. 图解大端与小端

在这里插入图片描述

图示说明

  • 左侧为小端模式,低地址存储低字节( 0 x 05 0\text{x}05 0x05),高地址存储高字节( 0 x 02 0\text{x}02 0x02)。
  • 右侧为大端模式,低地址存储高字节( 0 x 02 0\text{x}02 0x02),高地址存储低字节( 0 x 05 0\text{x}05 0x05)。
  • 网络通信统一采用大端(高字节优先)。

结论:只要发送和接收时分别使用 htonl/ntohl 等转换函数,无论主机字节序为何,网络上数据的解释始终一致,通信即可正常进行!

6. C 语言的字节序转换函数

C 标准库 <arpa/inet.h> 提供四个常用函数:

  • htons / ntohs:用于主机字节序与网络字节序的 16 位数据(short 类型)转换。
  • htonl / ntohl:用于主机字节序与网络字节序的 32 位数据(long 类型)转换。

用法举例

#include <arpa/inet.h>
uint32_t host_val = 0x12345678;
uint32_t net_val = htonl(host_val);   // 转换为大端字节序
uint32_t back = ntohl(net_val);       // 转回主机字节序

7. 编程细节和常见误区

7.1 不同字节序导致的打印“异常”

struct in_addr addr;
inet_pton(AF_INET, "2.3.4.5", &addr.s_addr);
printf("%x\n", addr.s_addr);
  • 在小端主机上,内存顺序为 05 04 03 02,打印结果为 0 x 05040302 0\text{x}05040302 0x05040302
  • 若使用 ntohl(addr.s_addr) 后再打印,结果为 0 x 02030405 0\text{x}02030405 0x02030405

7.2 字节序与网络协议

所有涉及多字节数字的协议字段,均要求以网络字节序传输,典型如 IP 地址、端口号、文件长度等。

7.3 字节序只影响多字节类型

单字节类型(如 charuint8_t)不存在字节序问题。

8. 经验总结与实践

  1. 务必牢记:网络通信的数据需转换为网络字节序!
  2. 单字节数据无需考虑字节序,仅多字节类型需要转换。
  3. 本地计算和操作使用主机字节序,仅在收发数据前后考虑转换。
  4. C 语言的转换函数具有跨平台安全性,正确使用即可避免错误。
  5. 日常调试和分析数据包时,理解字节序有助于排除诸多“显示异常”的假象。

9. 总结

字节序本质是数据在内存中的排列顺序:大端为高字节在低地址,小端为低字节在低地址。网络字节序强制为大端,以确保不同机器间通信无歧义。绝大多数设备本地采用小端字节序,因此必须使用字节序转换函数保证数据在网络传输中的标准性


On Endianness

关于字节序

Karl Stenerud. Jul 5, 2021

Byte Endianness in computers has been a constant source of conflict for decades. But is there really a clear advantage to one over the other? Let’s explore together!
几十年来,计算机中的字节序问题一直是争论的焦点。但究竟哪种字节序更具优势呢?让我们一同深入探讨!

Origins of the Term

术语的由来

The terms “Little Endian” and “Big Endian” originate from Jonathan Swift’s 1726 novel “Gulliver’s Travels”. It tells of long strife culminating in a great and costly war between the empires of “Lilliput” and “Blefuscu”, because they disagreed about which end of a boiled egg to break for eating. The “Big-Endians” went with the Emperor of Blefuscu’s court, and the “Little-Endians” rallied to Lilliput.
“小端序”和“大端序”这两个术语源自乔纳森·斯威夫特1726年所著的小说《格列佛游记》。书中讲述了“利立浦特”和“不来夫斯库”两大帝国之间因吃鸡蛋时该打破哪一端的分歧,而引发了一场旷日持久、代价惨重的战争。“大端派”支持不来夫斯库皇帝的宫廷,而“小端派”则拥护利立浦特。img

The terms were adapted for computer science in 1980 by Danny Cohen in an Internet Experiment Note titled “On Holy Wars and a Plea for Peace”, describing the conflict over the different ways of arranging bits and bytes in memory as components of larger data types. For byte-oriented data, “Little Endian” places the least significant byte of the value at the lowest address, and “Big Endian” places the most significant byte at the lowest address.
1980年,丹尼·科恩在一篇名为《论圣战与和平的呼吁》的互联网实验报告中,将这两个术语引入计算机科学领域,用于描述在内存中以不同方式排列位和字节作为更大数据类型组件所引发的争议。对于面向字节的数据,“小端序”将值的最低有效字节置于最低地址,而“大端序”则将最高有效字节放在最低地址。

+0+1+2+3
Big Endian0xDE0xAD0xBE0xEF
Little Endian0xEF0xBE0xAD0xDE

Both approaches have their adherents, and many flame wars erupted as rival CPU architectures, data formats, and protocols jockeyed for supremacy.
这两种方式都有各自的拥护者,随着不同CPU架构、数据格式和协议之间的竞争,引发了诸多激烈的争论。

Origins of the Concept

概念的起源

The most common argument for big endian ordering in computers is that it matches the “natural order” for writing numbers. This makes it easier to read numbers in a hex dump, and is less of a cognitive load overall when humans are looking at the encoded data. But how exactly did this “natural order” come about?
计算机中采用大端序的一个常见理由是,它与人们书写数字的“自然顺序”相符。这使得在十六进制转储中读取数字更为便捷,并且当人们查看编码数据时,整体认知负担也会减轻。但这种“自然顺序”究竟是如何形成的呢?

Origins of our Numbering System

我们编号系统的起源

Our modern numbering system has its roots in the Hindu numbering system, which was invented somewhere between the 1st and 4th century. At the time, there were competing writing systems that went right-to-left and left-to-right, with right-to-left being favored where the numbering system we inherited was created. It therefore was convenient for numbers to also be written right-to-left.
我们现代的数字编号系统源于印度数字系统,该系统大约在公元1世纪至4世纪之间发明。当时,存在从右到左和从左到右两种书写系统,而我们所继承的数字系统正是在从右到左书写系统较为盛行的地区发展而来。因此,数字从右到左书写也就更为便利。

The Hindu numbering system further developed into the Hindu-Arabic decimal system around the 7th century and spread through the Arab world (right-to-left was also the dominant style there, and so this convention was kept). It was adopted into Arab mathematics by the 9th century, and then introduced to Europe via North Africa in the 10th century.
印度数字系统在7世纪左右进一步演变为印度 - 阿拉伯十进制系统,并传播至阿拉伯世界(在那里从右到左同样是主要的书写方式,因此这一惯例得以保留)。9世纪时,它被阿拉伯数学所采用,10世纪时经北非传入欧洲。

Although many writing directions developed in parallel (left-to-right, right-to-left, top-to-bottom), the Hindu-Arabic visual order was always preserved (high digits on the left or top, regardless of script direction) in order to prevent confusion as globalization took root. So if your script direction was right-to-left, you wrote numbers in little endian order, and for all others you wrote them in big endian order.
尽管多种书写方向并行发展(从左到右、从右到左、从上到下),但为了在全球化进程中避免混淆,印度 - 阿拉伯数字的视觉顺序始终保持不变(无论书写方向如何,高位数字总是在左或在上)。因此,如果书写方向是从右到左,数字按小端序书写;而对于其他书写方向,则按大端序书写。

You can see some vestiges of the little endian origins in the German language: “ein und dreißig” (one and thirty - 31). By the time larger numbers become commonplace, the transition to the big endian way of saying numbers was already underway, making for some confusing word patterns: “neunhundert ein und dreißig” (nine hundred, one and thirty - 931).
在德语中,你可以看到一些小端序起源的痕迹,例如“ein und dreißig”(一和三十 - 31)。当较大数字变得普遍时,向大端序表述数字的方式转变已经开始,这就产生了一些令人困惑的词汇模式,如“neunhundert ein und dreißig”(九百、一和三十 - 931)。

Thus, our concept of “natural” number endianness turns out to be a cultural artifact resulting from a backward compatibility issue when applying a right-to-left numbering system to a left-to-right or top-to-bottom writing system.
因此,我们所谓“自然”的数字字节序概念,实际上是由于将从右到左的编号系统应用于从左到右或从上到下的书写系统时,为解决向后兼容性问题而产生的一种文化产物。

Consequences of Endianness on a Numbering System

字节序对编号系统的影响

What happens when a culture adopts a particular endianness for writing numbers? Let’s have a look.
当一种文化采用特定的字节序来书写数字时,会发生什么情况呢?让我们一起来看看。

Consider the following list of numbers in our current way of writing them in a left-to-right writing system (big endian):
以我们当前从左到右(大端序)的书写系统来考虑以下数字列表:

╔═════════════════════════════════╗
║  1839555                        ║
║    84734                        ║
║ 67526634                        ║
║      495                        ║
║        2                        ║
║    20345                        ║
╚═════════════════════════════════╝
         ⬆
         "Ones" digit

The numerals here are ordered the way we expect them to be ordered: with the most significant digit on the left and the least significant digit on the right. All of the numbers need to be aligned along the “ones” digit so that they can be easily compared.
这里的数字按照我们预期的顺序排列:最高有效位在左,最低有效位在右。所有数字都需要以“个位”数字对齐,以便于比较。

To write such numbers in a left-to-right system, we must first estimate how much room the digits could take, and then pre-emptively push to the right so that our “ones” column can remain aligned vertically.
要在从左到右的系统中书写这些数字,我们必须首先估计数字可能占用的空间,然后预先向右留出空位,以使“个位”列保持垂直对齐。

Notice how I had to use a preformatted section with a monospaced font so that I could add a bunch of spaces to align the numbers properly!
请注意,我必须使用等宽字体的预格式化区域,以便通过添加空格来正确对齐数字!

In a right-to-left writing system, the “ones” digits would be aligned along the margin (to the right) and would grow outwards (left) into the free space (which is why ledger pages are arranged this way).
在从右到左的书写系统中,“个位”数字将沿着页边(右侧)对齐,并向左延伸到空白空间(这就是账本页面如此排列的原因)。

╔═════════════════════════════════╗
║                         1839555 ║
║                           84734 ║
║                        67526634 ║
║                             495 ║
║                               2 ║
║                           20345 ║
╚═════════════════════════════════╝
                                ⬆
                                "Ones" digit

To better visualize this, observe the numerals with digit endianness reversed for left-to-right readers:
为了更直观地展示,对于从左到右阅读的人,观察数字字节序颠倒的情况:

╔═════════════════════════════════╗
║ 5559381                         ║
║ 43748                           ║
║ 43662576                        ║
║ 594                             ║
║ 2                               ║
║ 54302                           ║
╚═════════════════════════════════╝
  ⬆
  "Ones" digit

In this way of writing, “594” represents “five and ninety and four hundred”.
在这种书写方式中,“594”表示“5、90、400”。

Notice how the “ones” digits naturally align to the left margin. There’s no need to pre-emptively space anything; just write the numbers, no matter how long they get.
请注意,“个位”数字自然地与左边距对齐。无需预先留出空位,只需直接书写数字,无论其长度如何。

Everyone remembers the hassles of doing long multiplication, right?
大家都记得做长乘法时的麻烦,对吧?

╔═════════════════════════════════╗
║   2 4 1 6 5                     ║
║ ×   3 8 4 1                     ║
║ ───────────                     ║
║                                 ║
╚═════════════════════════════════╝

Before you even start, you have to think about how much room you’ll eventually need, because if you get it wrong you’ll end up running into the left margin:
在开始之前,你必须考虑最终需要多少空间,因为如果估计错误,最终可能会超出左边距:

╔═════════════════════════════════╗
║         2 4 1 6 5               ║
║       ×   3 8 4 1               ║
║       ───────────               ║
║         2 4 1 6 5               ║
║ +     9 6 6 6 0                 ║
║ + 1 9 3 3 2 0                   ║
║ + 7 2 4 9 5                     ║
║ ─────────────────               ║
║   9 2 8 1 7 7 6 5               ║
╚═════════════════════════════════╝

But what if the numbers were written in little endian order?
但如果数字按小端序书写会怎样呢?

╔═════════════════════════════════╗
║   5 6 1 4 2                     ║
║ × 1 4 8 3                       ║
║ ───────────                     ║
║   5 6 1 4 2                     ║
║ +   0 6 6 6 9                   ║
║ +     0 2 3 3 9 1               ║
║ +       5 9 4 2 7               ║
║ ─────────────────               ║
║   5 6 7 7 1 8 2 9               ║
╚═════════════════════════════════╝

Now suddenly it’s no problem. The empty space is to the right, and the numbers also grow to the right! Instead of fighting the writing system order, numbers now flow with it.
现在,突然就没有问题了。空白空间在右侧,数字也向右增长!数字不再与书写系统的顺序相悖,而是顺应其方向。

So while everyone of course considers their culture’s number endianness to be “natural”, there is a distinct advantage to writing numbers in little endian order. And in fact, that’s the way they were first invented!
所以,尽管每个人都认为自己文化中的数字字节序是“自然的”,但按小端序书写数字确实具有明显的优势。事实上,数字最初就是这样发明的!

So What?

那又怎样?

What does this have to do with endianness in computers? We don’t have “space to the right” to be mindful of, so none of the arguments about number endianness in meatspace would seem to apply to computers at first glance. But computers and data formats do have characteristics that endianness can take advantage of.
这与计算机中的字节序有什么关系呢?我们在计算机中无需考虑“右侧空间”,所以乍一看,现实世界中关于数字字节序的讨论似乎并不适用于计算机。然而,计算机和数据格式确实具有一些特性,字节序可以加以利用。

Endianness in Computers

计算机中的字节序

When dealing with computers, matching endianness to our way of visualizing numbers would be argument enough if the decision were otherwise arbitrary. Even if our system is backwards, keeping things consistent between computers and the more dominant left-to-right scripts is at least a small advantage.
在计算机领域,如果字节序的选择没有其他特殊依据,那么将其与我们可视化数字的方式相匹配,这一理由就已足够。即便我们的系统可能存在一定的“逆向性”,但保持计算机与更为普遍的从左到右书写方式之间的一致性,至少也有一定的好处。

So what exactly are the advantages each endian order enjoys in computing?
那么在计算过程中,每种字节序究竟有哪些优势呢?

Advantages

优势

Detecting odd/even

检测奇数/偶数

With big endian order, you need to check the last byte. With little endian order, you check the first byte.
使用大端序时,需要检查最后一个字节;而使用小端序时,检查第一个字节。

Big Endian:    8f 31 aa 9e c2 5a 1b 3d
                                     ^ 3d is odd

Little Endian: 3d 1b 5a c2 9e aa 31 8f
                ^ 3d is odd

Advantage: Little Endian
优势:小端序

Detecting sign

检测符号

With little endian order, you need to check the last byte. With big endian order, you check the first byte.
使用小端序时,需要检查最后一个字节;使用大端序时,检查第一个字节。

Big Endian:    8f 31 aa 9e c2 5a 1b 3d
               ^ 8f has sign "negative"

Little Endian: 3d 1b 5a c2 9e aa 31 8f
                                    ^ 8f has sign "negative"

Advantage: Big Endian
优势:大端序

Recasting a pointer

重新转换指针

Recasting pointers involves interpreting a memory location as different types. For example, taking a memory location that contains a 32-bit integer and recasting the pointed-to value as a 16-bit integer. Recasting pointers isn’t very common in higher level languages, but it happens often in compilers and assemblers.
重新转换指针指的是将内存位置解释为不同的数据类型。例如,将包含32位整数的内存位置重新解释为16位整数。在高级语言中,重新转换指针并不常见,但在编译器和汇编器中却经常发生。

With big endian order, you must adjust the pointer’s address to match the beginning of a different sized type. With little endian, a recast degenerates into a no - op.

|    Big Endian value 1         |    Little Endian value 1      |
|                               |                               |
|    +0 +1 +2 +3 +4 +5 +6 +7    |    +0 +1 +2 +3 +4 +5 +6 +7    |
|                               |                               |
|    |       32 bits       |    |    |       32 bits       |    |
|    00 00 00 00 00 00 00 01    |    01 00 00 00 00 00 00 00    |
|                               |                               |
|                | 16 bits |    |    | 16 bits |                |
|    00 00 00 00 00 00 00 01    |    01 00 00 00 00 00 00 00    |

Advantage: Little Endian
优势:小端序

Arbitrary precision numbers and arithmetic

任意精度数字和算术

Arbitrary precision numbers (aka big integers) are composed of integer elements in an array, which allows computing with values greater than the largest discrete integer type. Nowadays, the most common discrete type used for big integer arrays is uint32 or uint64.

| u64 | u64 | u64 |...

任意精度数字(也称为大整数)由数组中的整数元素组成,这使得可以使用大于最大离散整数类型的值进行计算。如今,用于大整数数组的最常见离散类型是uint32uint64

| u64 | u64 | u64 |...

The first element contains the lowest 64 bits of data, followed by the next higher 64 bits of data, and so on (little endian ordering). The reason for this is because, as carry values spill over, it’s simply added or removed from the next element in the array.

第一个元素包含最低的64位数据,随后是下一个更高的64位数据,依此类推(小端序)。这样做的原因是,当进位值溢出时,只需在数组的下一个元素中添加或减去该进位值。

If the elements were ordered big endian, you’d have to do a bunch of shifts and masks to correct everything whenever a carry occurred. The more array entries are in play, the more correction operations you’d have to do. You also have to shift everything over whenever the number grows bigger (basically the computer equivalent of shifting the whole number over when we run out of left - margin space on paper).

如果元素按大端序排列,每当发生进位时,就必须执行一系列的移位和掩码操作来纠正所有内容。涉及的数组条目越多,需要执行的纠正操作就越多。而且,每当数字增大时,还必须移动所有内容(基本上就相当于在纸上写数字时,左边空间不够了,要把整个数字往左移)。

Although this scheme can be realized with either byte order, there is an extra advantage to little endian byte ordering: If the CPU is little endian, you wouldn’t even need to care about the element size in the array because the bytes would naturally arrange themselves smoothly in little endian order across the entire array. Thus you could perform arithmetic using a single byte - by - byte algorithm, regardless of the actual element size (some little endian CPUs even have special multibyte instructions to help with this).

尽管这种方案可以通过任一字节序实现,但小端字节序有一个额外的优势:如果CPU是小端序的,你甚至无需关心数组中的元素大小,因为字节会自然地以小端序在整个数组中平滑排列。因此,无论实际元素大小如何,都可以使用单个逐字节算法执行算术运算(一些小端序的CPU甚至有特殊的多字节指令来辅助这一过程)。

OrderingElement 0Element 1Byte - by - byte?
Bigb7 b6 b5 b4 b3 b2 b1 b0b7 b6 b5 b4 b3 b2 b1 b0No
Littleb0 b1 b2 b3 b4 b5 b6 b7b0 b1 b2 b3 b4 b5 b6 b7Yes

Advantage: Little Endian
优势:小端序

Arbitrary length encodings

任意长度编码

Arbitrary length encodings such as [VLQ](https://en.wikipedia.org/wiki/Variable - length_quantity) allow for lightweight compression of integer values. The idea is that integers most often have the upper bits cleared, and so they don’t actually need the full integer width in order to be accurately represented.

像可变长度量(VLQ)这样的任意长度编码,允许对整数值进行轻量级压缩。其原理是整数的高位通常为零,因此实际上并不需要完整的整数宽度来准确表示。

VLQ uses big endian ordering, which works fine when representing values up to the maximum bit width of the architecture, but once you start storing larger values that would require big int types, you run into problems:

VLQ使用大端序,在表示不超过架构最大位宽的值时,这种方式运行良好,但一旦开始存储需要大整数类型的更大值时,就会遇到问题:

For simplicity and real estate given this blog format, let’s assume a CPU with a word size of 8 bits, but the same concepts apply to bigger word sizes too. To store larger values, we use a big int type, which requires arranging the words into an array in little endian order. Since VLQ is encoded in big endian order, the first chunk marks the highest bit position, and we work our way down from there.

为了在本博客格式下简单说明并节省篇幅,假设CPU的字长为8位,但相同的概念也适用于更大的字长。为了存储更大的值,我们使用大整数类型,这需要以小端序将字排列成数组。由于VLQ采用大端序编码,第一个块标记最高位位置,然后从那里开始向下处理。

First chunk (continuation bit “on”, so more chunks coming):

第一个块(延续位“开启”,表示还有更多块):

   W0 Bits                  W1 Bits
|  7  6  5  4  3  2  1  0 | 7  6  5  4  3  2  1  0 |
|    H6 H5 H4 H3 H2 H1 H0 |                        |

For the next chunk, we need to shift the high bits over into the next element to make room for the next set of bits (sound familiar?).

对于下一个块,我们需要将高位移动到下一个元素中,为下一组位腾出空间(听起来熟悉吗?)。

Second chunk (continuation bit “off”, so this is the end):

第二个块(延续位“关闭”,表示这是最后一个块):

   W0 Bits                  W1 Bits
|  7  6  5  4  3  2  1  0 | 7  6  5  4  3  2  1  0 |
| H0 L6 L5 L4 L3 L2 L1 L0 |      H6 H5 H4 H3 H2 H1 |

Shifting across only one element required:

仅需在一个元素间移动:

  • Copying W0 to W1 将W0复制到W1
  • Shifting W1 right by 1 将W1右移1位
  • Shifting W0 left by 7 将W0左移7位
  • Logical OR next 7 bits from the next VLQ element into W0 将下一个VLQ元素的后7位与W0进行逻辑或运算

And this gets worse the more elements are in play.

涉及的元素越多,情况就越糟糕。

Now let’s see how it would work if the data were encoded using LEB128 (basically the same thing, but in little endian order):

现在让我们看看如果使用 LEB128(基本相同,但采用小端序)对数据进行编码会怎样:

*First chunk (continuation bit “on”, so more chunks coming):

第一个块(延续位“开启”,表示还有更多块):

   W0 Bits                  W1 Bits
|  7  6  5  4  3  2  1  0 | 7  6  5  4  3  2  1  0 |
|    L6 L5 L4 L3 L2 L1 L0 |                        |

For the next chunk, we split the value across two elements.

对于下一个块,我们将值分布在两个元素中。

Second chunk (continuation bit “off”, so this is the end):

第二个块(延续位“关闭”,表示这是最后一个块):

   W0 Bits                  W1 Bits
|  7  6  5  4  3  2  1  0 | 7  6  5  4  3  2  1  0 |
| H0 L6 L5 L4 L3 L2 L1 L0 |      H6 H5 H4 H3 H2 H1 |

Splitting the last VLQ element required:

拆分最后一个VLQ元素所需操作:

  • Logical OR W0 with (element«7) 将W0与(element左移7位)进行逻辑或运算
  • Copy (element»1) to W1 将(element右移1位)复制到W1

And this remains at constant complexity no matter how many elements are in play.

无论涉及多少元素,其复杂度都保持不变。

Bonus: It’s also possible to do arithmetic in LEB128 while it remains encoded!

额外好处:还可以在LEB128保持编码的情况下进行算术运算!

Aside: Did you notice how disjointed it looks listing the high bits to the left in a left - to - right system? It would have flowed much better were they listed this way:

旁白:你有没有注意到在从左到右的系统中,将高位列在左边看起来多么不协调?如果这样列会顺畅得多:

   W0 Bits                  W1 Bits
|  0  1  2  3  4  5  6  7 |  0  1  2  3  4  5  6  7 |
| L0 L1 L2 L3 L4 L5 L6 H0 | H1 H2 H3 H4 H5 H6       |

Advantage: Little Endian

优势:小端序

Variable length encoding with a prepended length field

带有前置长度字段的可变长度编码

A common encoding trick is to reserve a few bits of the encoded space to store the length of the field (low bits of the low byte if little endian, high bits of the high byte if big endian).

一种常见的编码技巧是预留编码空间的几位来存储字段的长度(如果是小端序,为低字节的低位;如果是大端序,为高字节的高位)。

(XXXXXX LL) (XXXXXXXX) (XXXXXXXX) (XXXXXXXX)...
                   - or -
(LL XXXXXX) (XXXXXXXX) (XXXXXXXX) (XXXXXXXX)...

Generally with this encoding pattern, the two bits LL represent the total size of the data.

通常,在这种编码模式中,两位LL表示数据的总大小。

Example:

LLBytesData Size
0016 bits
01214 bits
10430 bits
11862 bits

If this data is encoded in little endian byte order, the decoding routine is pretty easy:

如果此数据以小端字节序编码,解码程序相当简单:

  • Read the first byte and extract the length field. 读取第一个字节并提取长度字段。
  • For each byte to be read: 对于每个要读取的字节:
    • Read the byte into the current position. 将字节读入当前位置。
    • Increment current position by 1 byte. 将当前位置增加1字节。
  • Shift the result right 2 bits. 将结果右移2位。

You can simply decode the whole thing and then shift the decoded value two bits to the right because the size field is always in the two lowest bits of the value, regardless of the size.

你可以简单地解码整个内容,然后将解码后的值右移两位,因为无论数据大小如何,大小字段总是位于值的最低两位。

If the CPU is also little endian, the algorithm gets even simpler:

如果CPU也是小端序,算法会更简单:

  • Read the first byte to extract the length field. 读取第一个字节以提取长度字段。
  • Read the same address again using the correct sized load instruction (recasting the pointer). 使用正确大小的加载指令(重新转换指针)再次读取相同地址。
  • Shift the result right 2 bits. 将结果右移2位。

If the encoding used big endian order, the length field would be at the top of the high byte, which means that you’d need to mask out a different part of the decoded value depending on the data size.

如果编码采用大端序,长度字段将位于高字节的顶部,这意味着你需要根据数据大小屏蔽解码值的不同部分。

Advantage: Little Endian
优势:小端序

Convention

惯例

Most network protocols and formats until recently have been big endian (Network Byte Order).

直到最近,大多数网络协议和格式都采用大端序(网络字节序)。

Advantage: Big Endian
优势:大端序

“Naturalness”

“自然性”

Binary dumps look more in line with how humans with left - to - right scripts expect to read numbers.

二进制转储看起来更符合从左到右书写的人对数字的阅读习惯。

Advantage: Big Endian
优势:大端序

Sorting unknown uint - struct blobs

对未知的无符号整数结构体数据块进行排序

If you have a series of unknown blobs, and know only that the internal structure has only unsigned integers stored in descending order of significance, you can sort these blobs without knowing what the actual structure is.

如果你有一系列未知的数据块,并且只知道其内部结构仅包含按重要性降序存储的无符号整数,那么你可以在不知道实际结构的情况下对这些数据块进行排序。

Yeah, that’s a pretty rare thing, but technically it’s an advantage, and someone, somewhere is using it!

是的,这相当罕见,但从技术角度来说,这是一个优势,而且肯定有人在某个地方使用它!

Advantage: Big Endian
优势:大端序

Conclusion

结论

The big endian advantages tend to be cosmetic or convention based, or of minor usefulness. The little endian advantages offer real world performance boosts in many cases (given the right algorithm or encoding to take advantage of it).

大端序的优势往往更多体现在表面或基于惯例,实际用途相对较小。而小端序的优势在许多情况下能够切实提升性能(前提是采用合适的算法或编码来利用这些优势)。

Big Endian Advantages

大端序优势

  • Detecting sign 检测符号
  • Convention 惯例
  • “Naturalness” “自然性”
  • Sorting unknown uint - struct blobs 对未知的无符号整数结构体数据块进行排序

Little Endian Advantages

小端序优势

  • Detecting odd/even 检测奇数/偶数
  • Recasting a pointer 重新转换指针
  • Arbitrary precision numbers and arithmetic 任意精度数字和算术
  • Arbitrary length encodings 任意长度编码
  • Variable length encoding with a prepended length field 带有前置长度字段的可变长度编码

The biggest advantages go to little endian because little endian works best with “upward - growing” data (meaning data whose bits cluster to the low bits and grow into the upper empty bits). An example would be integers, which use higher and higher order bits as more digits are needed. And since integers are the most common data type in use, they offer the most opportunities.

小端序具有最大的优势,因为它最适合处理“向上增长”的数据(即数据的位聚集在低位,并向高位的空位扩展)。整数就是一个例子,随着需要表示的数值增大,会使用越来越高的位。由于整数是最常用的数据类型,所以小端序有更多发挥优势的机会。

Big endian ordering works best with “downward - growing” data. An example would be floating point, which in some little endian architectures is actually stored in big endian byte order (although this practice is not very common due to portability issues).

大端序最适合处理“向下增长”的数据。例如浮点数,在一些小端序架构中,浮点数实际上是以大端字节序存储的(不过由于可移植性问题,这种做法并不常见)。


via :

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值