Skip to content

操作系统-大端序和小端序

[!cite] > 字节序探析:大端与小端的比较 - 阮一峰的网络日志

1. 什么是大端序和小端序?

  • 大端序:高位字节放在低位地址当中符合人类的阅读习惯。(先读大端,又名为网络序
  • 小端序:低位字节放在高位地址当中符合计算机的读写特性。(先读小端,又名位字节序

image.png

  • 举一个具体的例子进行说明,对于一个16进制的数字 0x0a0b0c0d 而言,其最低位为 0d,最高位为 0a。换言之,小端为 0d,大端为 0a小端序就是小端放低位,大端序就是大端放低位。
  • 按照我们人类的习惯,如果将其记录在纸上,那么很自然地将其书写为 0a0b0c0d。但是,如果将其记录在计算机当中,实际上有两种可能的字节序。
  • 我们需要 4 个字节去存储这个 8 位的16进制数,如上图所示。在内存当中,这 4 个字节的顺序分别是 0\1\2\3,其中,我们将内存低位标记为 1, 内存高位标记为 4
  • 那么,如果是小端序,那么小端放在内存低位 1,在例子中,就是 0d 这两个字节放在 1 位置。如果是大端序,那么大端 0a 放在内存低位 1,这和我们人类的习惯一致。

2. 为什么我们需要大端序和小端序?

  • 对于人类而言,显然是大端序符合日常从左到右的阅读习惯。
  • 但是对于计算机而言,比如需要比较两个数字的大小,或者是运行乘法计算的时候,那么小端序的执行效率高得多。
  • 计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。但是,人类还是习惯读写大端字节序。
  • 所以,除了计算机的内部处理,其他的场合比如网络传输和文件储存,几乎都是用的大端字节序。 正是因为这些原因才有了字节序。
  • 下面两张图,第一张图表示大端序存放数字,内存从数字的低位向高位读,要全部遍历完才能判定大小。第二张图表示小端序存放数字,内存从数字的高位向低位读,只要判定高位即可判定大小。

image.png

image.png


  • 一般来说,文件系统当中采取大端序,cpu 之类的单元采取小端序。
  • 比如在 archLinux 上面打印出来的结果

image.png

3. Go语言当中的一些实现`

go
package main

import (
 "encoding/binary"
 "fmt"
 "unsafe"
)

const INT_SIZE = int(unsafe.Sizeof(0)) //64位操作系统,8 bytes

//判断我们系统中的字节序类型
func systemEdian() {

 var i = 0x01020304
 fmt.Println("&i:",&i)
 bs := (*[INT_SIZE]byte)(unsafe.Pointer(&i))

 if bs[0] == 0x04 {
  fmt.Println("system edian is little endian")
 } else {
  fmt.Println("system edian is big endian")
 }
 fmt.Printf("temp: 0x%x,%v\n",bs[0],&bs[0])
 fmt.Printf("temp: 0x%x,%v\n",bs[1],&bs[1])
 fmt.Printf("temp: 0x%x,%v\n",bs[2],&bs[2])
 fmt.Printf("temp: 0x%x,%v\n",bs[3],&bs[3])

}


func testBigEndian() {

 var testInt int32 = 0x01020304
 fmt.Printf("%d use big endian: \n", testInt)
 testBytes := make([]byte, 4)
 binary.BigEndian.PutUint32(testBytes, uint32(testInt))
 fmt.Println("int32 to bytes:", testBytes)
 fmt.Printf("int32 to bytes: %x \n", testBytes)

 convInt := binary.BigEndian.Uint32(testBytes)
 fmt.Printf("bytes to int32: %d\n\n", convInt)
}

func testLittleEndian() {

 var testInt int32 = 0x01020304
 fmt.Printf("%x use little endian: \n", testInt)
  testBytes := make([]byte, 4)
 binary.LittleEndian.PutUint32(testBytes, uint32(testInt))
 fmt.Printf("int32 to bytes: %x \n", testBytes)

 convInt := binary.LittleEndian.Uint32(testBytes)
 fmt.Printf("bytes to int32: %d\n\n", convInt)
}

func main() {
 systemEdian()
 fmt.Println("")
 testBigEndian()
 testLittleEndian()
}