為什么要有字節(jié)序?用 Go 解釋下
什么是字節(jié)序
字節(jié)序,又稱端序或尾序(英語中用單詞:Endianness 表示),在計算機領域中,指電腦內存中或在數字通信鏈路中,占用多個字節(jié)的數據的字節(jié)排列順序。
在幾乎所有的平臺上,多字節(jié)對象都被存儲為連續(xù)的字節(jié)序列。例如在 Go 語言中,一個類型為int的變量x地址為0x100,那么其指針&x的值為0x100。且x的四個字節(jié)將被存儲在內存的0x100, 0x101, 0x102, 0x103位置。
字節(jié)的排列方式有兩個通用規(guī)則:
大端序(Big-Endian)將數據的低位字節(jié)存放在內存的高位地址,高位字節(jié)存放在低位地址。這種排列方式與數據用字節(jié)表示時的書寫順序一致,符合人類的閱讀習慣。 小端序(Little-Endian),將一個多位數的低位放在較小的地址處,高位放在較大的地址處,則稱小端序。小端序與人類的閱讀習慣相反,但更符合計算機讀取內存的方式,因為CPU讀取內存中的數據時,是從低地址向高地址方向進行讀取的。
上面的文字描述有點抽象,我們拿一個例子來解釋一下字節(jié)排列時的大端序和小端序。
在內存中存放整型數值168496141 需要4個字節(jié),這個數值的對應的16進制表示是0X0A0B0C0D,這個數值在用大端序和小端序排列時的在內存中的示意圖如下:

為何要有字節(jié)序
很多人會問,為什么會有字節(jié)序,統(tǒng)一用大端序不行嗎?答案是,計算機電路先處理低位字節(jié),效率比較高,因為計算都是從低位開始的。所以,計算機的內部處理都是小端字節(jié)序。在計算機內部,小端序被廣泛應用于現(xiàn)代 CPU 內部存儲數據;而在其他場景,比如網絡傳輸和文件存儲則使用大端序。
Go語言對字節(jié)序的處理
Go 語言存儲數據時的字節(jié)序依賴所在平臺的 CPU,處理大小端序的代碼位于 encoding/binary ,包中的全局變量BigEndian用于操作大端序數據,LittleEndian用于操作小端序數據,這兩個變量所對應的數據類型都實現(xiàn)了ByteOrder接口。
package main
import (
"encoding/binary"
"fmt"
"unsafe"
)
const INT_SIZE = int(unsafe.Sizeof(0)) //64位操作系統(tǒng),8 bytes
//判斷我們系統(tǒng)中的字節(jié)序類型
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()
}
運行上面的程序會在終端里輸出
&i: 0xc000084000
system edian is little endian
temp: 0x4,0xc000084000
temp: 0x3,0xc000084001
temp: 0x2,0xc000084002
temp: 0x1,0xc000084003
16909060 use big endian:
int32 to bytes: [1 2 3 4]
int32 to bytes: 01020304
bytes to int32: 16909060
1020304 use little endian:
int32 to bytes: 04030201
bytes to int32: 16909060
總結
計算機電路先處理低位字節(jié),效率比較高,因為計算都是從低位開始的。所以,計算機的內部處理都是小端字節(jié)序。但是,人類還是習慣讀寫大端字節(jié)序。所以,除了計算機的內部處理,其他的場合比如網絡傳輸和文件儲存,幾乎都是用的大端字節(jié)序。正是因為這些原因才有了字節(jié)序。
計算機處理字節(jié)序的時候,如果是大端字節(jié)序,先讀到的就是高位字節(jié),后讀到的就是低位字節(jié)。小端字節(jié)序則正好相反。
推薦閱讀
