基本理解是,字节(十六进制 xx )是最小的处理单位。
如果一个数字是 4 个字节,假设是0x12345678
,假设通过网络传输过来后,计算机
12 | 34 | 56 | 78
的顺序存储这个数字78 | 56 | 34 | 12
的顺序存储这个数字。计算机是从低电路读取数据的。上面的78 | 56 | 34 | 12
和78 | 56 | 34 | 12
,从左往右,电位是逐渐递增的,也就是读的顺序是从左读到右。所以大端下,计算机先读数字的高位,小端下先读数字的低位。
存储数字是这样的。但有疑问:
0x1234
和0x5678
,最后小端的存储内容是12 | 34 | 56 | 78
吗?小端是34 | 12 | 78 | 56
还是 78 | 56 | 34 | 12
?大小端咋区分这两个数字(怎么知道它是两个,而不是一个数字?) 1
Mitt 2021-10-20 15:06:31 +08:00
按我的理解大小端也是个约定,跟字节流无关,你写和读的时候才需要区分,存储不需要
两个数字的问题你的场景其实是连续内存,那么两个数字可以按每个数字去看就是 34 | 12 | 78 | 56,当然前提是 int16,如果是 int32 就应该是 34 | 12 | 00 | 00 和 00 | 00 | 78 | 56 |
2
imes 2021-10-20 15:11:44 +08:00
让我想起来我之前被 Endianness 坑过,摄像头是 Big-End,主控是 Little-End
|
3
x97bgt OP |
4
Mitt 2021-10-20 15:17:54 +08:00
@x97bgt #3 这是内存布局,没有大小端区别,你从 buffer 读始终是从左到右一位一位读的,另外数字是的,每个数字也就是二进制长度都是固定的,如果不固定常见的作法是前面加一个标识接下来要读多少个字节,而数字对应的编程是 int 有 int32 64 8 等等,int8 就是 byte 也就 8 个 bit,存储的时候没有什么整数浮点区分,都是 bytes
|
5
leoleoasd 2021-10-20 15:18:38 +08:00
数据就是 bit stream,没有类型,可以按照任意类型解读。
如果对于以下数据(从左到右地址从低到高,最左边是 0 ): 0x12 0x34 0x56 0x78 那么从地址 0 读 int ( 4 字节),大端序机器都出来就是 12345678,小端序机器都出来就是 78563412 。 从地址 0 读 short ( 2 字节),分别就是 1234 和 3412. 从地址 1 读 short ( 2 字节),分别就是 3456 和 5634. 你的问题:如何区分两个数字(怎么知道是两个,不是一个):没法区分。所以要约定好是几个。只要数据发送方和接受方约定好是两个,按照两个存,按照两个取,就不会有问题。 储存规则(包括硬盘、内存、网络)和上层逻辑无关,只要存和取用的统一套规则就不会出问题。 |
6
chanchancl 2021-10-20 15:19:29 +08:00
大小端只是你已知数据,该如何进行存储和传送的问题(比如内存、硬盘、网络)
如何区分数字?存储系统本质上存的都是 01 序列,是不管区分的,区分靠的是你上层的 protocol 对于字节流,没必要区分,因为计算机存储的最小寻址单位就是字节了 |
7
jorneyr 2021-10-20 15:40:41 +08:00
怎么知道它是两个,而不是一个数字?
这个属于解析了,只有在写程序的时候才知道,也就是写程序的时候你很明确的说读取一个 32 位 int 还是读取一个 64 位 int,存储是没有逻辑的,使用才有逻辑,例如 Java 里的 DataInputStream.readInt() 和 DataInputStream.readLong()。 |
8
x97bgt OP @Mitt @leoleoasd @chanchancl
「机器读出来」,这句话具体表示什么? 是机器把数据把数据从磁盘里读到内存 /寄存器? 大小端是表示内存的字节顺序有大小端之分吗?磁盘里存储的东西都一样。但读出来后,内存里存放的字节顺序,取决于大小端的不同。这样理解对么? |
9
leoleoasd 2021-10-20 16:06:44 +08:00
|
11
dong568789 2021-10-20 17:28:22 +08:00
你理解的没错,int 类型的大端和小端在磁盘上的顺序是不一致的,所以才需要双方协商好编 /解码的 protocol
|
12
araaaa 2021-10-20 17:34:39 +08:00
只有整型才有大小端
|
13
index90 2021-10-20 17:40:04 +08:00
1. 小端是 34 | 12 | 78 | 56
|
14
index90 2021-10-20 17:47:06 +08:00
1. 小端是 34 | 12 | 78 | 56 ;如何区分两个数字与大小端无关,与你用的类型有关。如果你从地址 0 读取两个 int16,那么就是两个数字,如果你从地址 0 读取一个 int32,那么就是一个数字
2. 有,记得数据发送时从高地址位先行,接收端也是从高地址位开始接收。 |
16
littlefishcc 2021-10-20 18:38:16 +08:00
大小端是电脑对 short 和 int 读写处理规则而已,这个跟 JSON 和 xml 解析类似的原理,只是大小端针对二进制数据处理,所以不是那么直观理解。
这种描述不是很好理解,你可以写代码测试。 C 语言 int i = 0x12345678;// 如果小端 CPU 跑的程序内存二进制是:0x78562312 int i = 0x12345678;// 如果大端 cpu 跑的程序内存二进制值是:0x12345678 int i = 0x12345678; unsigned char* c = (unsigned char*)&(i); for( int n = 0; n < 4; n++){ printf("%x",c[n]); } window pc 电脑 一般是小端: 打印结果 :78563412 我的理解: 我们写代码 int i = xxxx,但编译器根据 cpu 类型换成对应的汇编代码。32 位的程序,汇编中有 word dword 来表示 2 个字节和 4 个字节。但由于历史发展原因,汇编表示 word dword 存放顺序不统一,才产生大小端由来,cpu 厂商不一样,想法一样,这个跟当初浏览器 保准不同一类似。 总结: 大小端问题其实汇编层问题,所以我们用高级语言时候是了解不到整数在具体存放方式,所以不是那么容易理解。 我们 char 根本没有大小端问题,因为汇编层只是针对 word dowd 存放不一致。但不要理解 int i = 0x12345678; writeFile((char*)&i); 这样子是没有解决内存的问题,而是你要把 i 分解成 char c1 = 0x12, c2 = 0x34, c3= 0x56, c4 = x078, 然后分别写入到字节流去。 如果你传递数据是 char 或者准确说字符串是不用关心大小端问题,你看 http 传送根本不用关心大小端,只有传递二进制 数据结构 { int id; int len; char [xxxx] packet; } 类似含有 short 和 int,long 字节形识,就要考虑大小端问题。 上面是我自己对大小端理解,可能一些解释不是很正确,麻烦谅解。 场景: 大小端一般用于网络通信,因为用户电脑 CPU 可能不一样,所以通信字节必须统一(可以统一为小端也可以统一为大端,看客服端与服务器约定),现在高级语言都有 buffer,对整数或者 long 或 short 进行大小端操作方法,非常方便。 |
17
xujinkai 2021-10-20 19:21:59 +08:00 via Android
字节流没有大小端。
数字的话,在写入和解析的时候你是知道长度的。你举的例子是两个两字节的数字,所以是第一个。如果是两个四字节的数字,最后就是 0x3412000078560000 。 至于如何确定到底是两字节还是四字节,那是提前约定好的。 |
18
ecnelises 2021-10-20 19:48:59 +08:00
大小端的最小单位是字节而不是位,而且针对的是内存操作中的单个数据,比如 int 、long 、double 等。所以如果你的数据单位也只有一个字节,就不用操心字节序问题。而多个 int 、long 组成的数组,元素之间的顺序也和字节序无关。
怎么理解呢?假设你有这样一个数组,其中两个 32 位 int,值分别是 0x01234567 和 0x89ABCDEF,在小端序机器上,内存里存的字节是 0x67 0x45 0x23 0x01 0xEF 0xCD 0xAB 0x89 ;而大端序上则是 0x01 0x23 0x45 … 0xCD 0xEF 所以你可以看到,大端序更直观,而两者各有各的好处。如今小端序更流行更像是 x86 造成的习惯原因。https://stackoverflow.com/questions/5185551 你想问的是,数据真正存储到文件里用的是什么字节序。严格来说这个问题本身是不存在的,因为字节序描述的是内存中的顺序。但不妨不准确地理解为大端序,因为从硬盘读取到内存里的都是一段一段的字节流,然后你可以随意「理解」为任意类型来读。 但是我们还是没有理解,为什么会有字节序这种东西存在,如何「理解」内存难道不是程序的自由吗?为什么要机器来规定?不是的。字节本身是没有意义的。两组 4 个字节,当作 float 和当作 int 进行相加,结果的字节是完全不一样的。所以机器需要一种确定的机制来从内存读数据到寄存器,或者直接从两个内存地址读数据进行操作。所以字节序只对机器指令集能够原生支持的数据类型有意义。 |
19
realradiolover 2021-10-20 21:13:42 +08:00
首先,从网卡、内存、磁盘的角度看,传输、保存的永远都是字节流。给字节流中的字节赋予意义,例如数据类型( 32 位整型、浮点数.....)、字符串、位图数据....是应用程序的责任。和数据类型配套衍生出来的是大小端规则。CPU 处理数据前,会一次性读入 16 位或者 32 位或者 64 位数据到高速缓存,再到 ALU 等。大端机 CPU 会认为低比特位存放数值高位,小段机正相反。为了适配 CPU 这种约定,保证 CPU“看到”逻辑上正确的数值,需要事先组织这个数值在内存里的排列。这是大小端机制的来源。
大小端在 bit 层次上就存在的。假设内存地址从左到右增加,0x3 在小端机器上保存为 11000000,在大端机上保存为 00000011,在以太网等电信网络设施中、USB 串口中,按照 0->0->0->0->0->0->1->1 的顺序发送。 所有网卡的驱动,如果主机是小端,会自动转换字节内部的 bit 序(网络传输顺序为大端序);但是 Byte 间的顺序(字节序)保持不变。毕竟网卡只能看到字节流,它不知道也不需要关心这些字节代表的是字符串,还是 uint32_t 。 字节序转换,是应用层的事情(例如常见的 ntohs,ntohl 调用)。字节内部的 bit 序已经由网卡处理好。 容易混淆的是,我们在分析类似问题的时候会用到 16 进制数字,经常把数学意义上的数字,和内存表示法意义上的数字书写混为一谈。数学意义上的数字的书写,永远是左边高位右边低位。内存表示法意义上的数字,一般是左边低地址右边高地址,所以大端小端,写出来是不一样的。 另一个容易混淆的是,我们一般书写 16 进制数字的最小单位是字节,而不是比特。这就意味这它无法表征字节内部的 bit 序。数字 0x1234,在大端机器上的内存表示为 0x12 0x34,在小端机器上的内存表示为 0x34 0x12 。而 0x34 这一个字节,在大端、小端、网络中的内存表示法是一致的:0x34 。如果用二进制内存表示法,区别就很明显了。 楼主的提问是应用程序对字节流的定义,和字节流本身的大小端机制并没有关系。 |
20
x97bgt OP |
21
x97bgt OP |
22
x97bgt OP @realradiolover
假设主机是 0101 1101 1010 0001,那网卡会把它变成 1011 0101 1000 0101 (每个字节内部的 bit 反置),然后送到网络上传输。你说的网卡转换 bit 序,是指这么一回事吗? 这个好像不是我所认识的大小端的定义了。。。。 |
23
x97bgt OP 上面写错了,是 0101 1101 1010 0001 -> 1011 1010 1000 0101
|
24
FACEB00K 2021-10-21 03:10:06 +08:00
Q: 小端是 34 | 12 | 78 | 56 还是 78 | 56 | 34 | 12
A: 小端是 34 12 78 56. 因为这是两个数字 Q: 大小端如何区分这两个数字 A: 因为是两个数字,所以程序里咱自己会向 buffer 中写两次啊, 收到数据的时候也应该从 buffer 中读两次 Q: 最后的问题 A: 字节流肯定是不会变的,发送什么数据接收到的就是什么数据。但你接受方要读两个字节的数据,现在你只收到一个,不也是要等到第二个收到了,才能构造出 short 类型的数据嘛 Q:什么时候会发生大小端问题 A:发送方大小端不一样,都用的 memcpy 来读写数据。实际开发的时候,定的协议里都会定某个字节是低自节还是高字节,然后可以显示地封装和显示地构造。buff[0] = num & 0xff; buff[1] = (num >> 8) & 0xff; |
25
msg7086 2021-10-21 05:00:26 +08:00
@x97bgt 网卡应该只吃 dumb 字节流。你发送的时候要自己根据协议把数据转换成字节流,然后接收方根据协议把字节流转回来。
当然通行的做法还有一律使用网络端序(大端序),免得两端扯皮。 |
26
littlefishcc 2021-10-21 23:49:32 +08:00
我看了好多回答,感觉本质为什么产生大小端问题还是没有答出来?
我们看的书籍都说跟 CPU 有关系,CPU 处理 机器码 是没有语言,没有类型可分,他只人 0 1,更加不存在 int short long 这些所谓类型逻辑,既然跟 CPU 有关系,CPU 执行机器码时候是不关心,所以只有在机器码上一层才会有可能,所以我在上面回答说 汇编层的问题, 汇编进行的操作数才有这个问题。汇编代码也跟 CPU 厂商有关系。intel 与 IBM 汇编代码实现不一样,导致操作数在内存不一样,所以才会出现大小端问题。 |