哈嘍,你們好,我是asong
。今天想與你們聊一聊計算機硬件中的兩種儲存數據的方式:大端字節序(big endian)、小端字節序(little endian)。老實說,我第一次知道這個概念仍是在學習單片機的時候,不過當時學完就忘了,真正長記性是在面試的時候,面試官問我:你能用C
語言寫段代碼判斷機器的字節序嗎?你必定好奇爲何要用C
語言寫,傻瓜,這是我大學的時候面試嵌入式崗位呀。扯遠啦,其實當時的我是懵逼的,早就忘了什麼大端、小端了,因此遺憾的錯過嵌入式行業,進入了互聯網行業(手動狗頭)。本文的全部代碼已經上傳
github
:https://github.com/asong2020/...;歡迎star
git因爲微信公衆號改版,文章推送會亂序,爲了第一時間收到
asong
的消息,請讀者朋友動動小手,在公衆號主頁右上角設置裏加個星標。感謝你們~。github
我一直都不理解,爲何要有大小端區分,尤爲是小端,老是會忘記,由於他不符合人類的思惟習慣,但存在即爲合理,存在就有他存在的價值。這裏有一個比較合理的解釋:計算機中電路優先處理低位字節,效率比較高,由於計算機都是從低位開始的,因此計算機內部處理都是小端字節序。可是咱們日常讀寫數值的方法,習慣用大端字節序,因此除了計算機的內部,其餘場景大都是大端字節序,好比:網絡傳輸和文件儲存時都是用的大端字節序。面試
因此大小端問題極可能與硬件或者軟件的創造者們有關,實際在計算機工業應用上,不一樣的操做系統和不一樣的芯片類型都有所不一樣。不一樣的系統設計不一樣,因此咱們也不必深究爲何要有這個區分,只須要知道他們的原理就行了。算法
大端模式:高位字節排放在內存的低地址端,低位字節排放在內存的高地址端;編程
小端模式:低位字節排放在內存的低地址端,高位字節排放在內存的高地址端;設計模式
這麼說也有點模糊,仍是配個圖來看更清晰:緩存
咱們來看一看數值0x1A2B3C4D
在大端與小端的表現形式,這裏咱們假設地址是從0x4000
開始:微信
上圖所示:大端和小端的字節序最小單位是1字節(8bit),大端字節序就和咱們平時的寫法順序同樣,從低地址到高地址寫入0x1A2B3C4D
,而小端字節序就是咱們平時的寫法反過來,由於字節序最小單位爲1
字節,因此從低地址到高地址寫入0x4D3C2B1A
。網絡
由於大端、小端很容易混記,因此分享一個我本身記憶的小技巧:架構
大端:高低高低,也就是高位字節排放在內存低地址端,高地址端存在低位字節;
小端:高高低低;也就是高位字節排放在內存的高地址端,低位字節排放在內存的低地址端;
Go
區分大小端計算機處理字節序的時候,不知道什麼是高位字節,什麼是低位字節。它只知道按順序讀取字節,先讀取第一個字節,再讀取第二個字節,因此說我就能夠根據這個特性來讀判斷大小端。
在使用Go
語言實現以前,仍是想再用C
語言實現一遍,由於這是我一輩子的痛,畢竟在面試的時候沒寫出來。
能夠利用C
語言中union
各字段共享內存的特性,union型數據所佔的空間等於其最大的成員所佔的空間,對 union 型的成員的存取都是相對於該聯合體基地址的偏移量爲 0 處開始,也就是聯合體的訪問不論對哪一個變量的存取都是從 union 的首地址位置開始,聯合是一個在同一個存儲空間裏存儲不一樣類型數據的數據類型。這些存儲區的地址都是同樣的,聯合裏不一樣存儲區的內存是重疊的,修改了任何一個其餘的會受影響。因此咱們可寫出代碼以下:
#include "stdio.h" // big_endian: 1 // little_endian: 2 int IsLittleEndian() { union { short value; char array[2]; } u; u.value = 0x0102; if (u.array[0] == 1 && u.array[1] == 2){ return 1; }else if (u.array[0] == 2 && u.array[1] == 1){ return 2; } return -1; } int main() { int res; res = IsLittleEndian(); printf("result is %d\n",res); if (res == 1) { printf("it is big endian"); } if (res == 2){ printf("it is little endian"); } return 0; } // 運行結果(不一樣系統運行結果會有不一樣) result is 2 it is little endian%
如今咱們來思考一下,怎麼用Go
語言驗證大小端,Go
中是沒有union
這個關鍵字,那就要另闢蹊徑,換一個方法來實現啦,咱們能夠經過將int32
類型(4字節)強制轉換成byte
類型(單字節),判斷起始存儲位置內容來實現,由於Go
不支持強制類型轉換,咱們能夠藉助unsafe
包達到咱們的要求,寫出代碼以下:
package main import ( "fmt" "unsafe" ) func IsLittleEndian() bool{ var value int32 = 1 // 佔4byte 轉換成16進制 0x00 00 00 01 // 大端(16進制):00 00 00 01 // 小端(16進制):01 00 00 00 pointer := unsafe.Pointer(&value) pb := (*byte)(pointer) if *pb != 1{ return false } return true } func main() { fmt.Println(IsLittleEndian()) } // 運行結果:ture
這裏你們可能會有疑惑,爲何要有大小端轉化,這是由於在涉及到網絡傳輸、文件存儲時,由於不一樣系統的大小端字節序不一樣,這是就須要大小端轉化,才能保證讀取到的數據是正確的。我在大學時作arm
和dsp
通訊的時候,就遇到個大小端轉換的問題,由於arm
是小端,dsp
是大端,因此在不瞭解這個知識點的時候,通訊的數據就是亂的,致使我調試了很久。
大小端的轉換其實還算比較簡單,經過位操做就能夠實現,這裏咱們用uint32
類型做爲例子:
func SwapEndianUin32(val uint32) uint32{ return (val & 0xff000000) >> 24 | (val & 0x00ff0000) >> 8 | (val & 0x0000ff00) << 8 | (val & 0x000000ff) <<24 }
是的,你沒看錯,就是這麼簡單,這裏也很簡單,就不細講了。
其實go
官方庫encoding/binary
中已經提供了大小端使用的庫,咱們要想進行大小端轉換,徹底可使用官方庫,不必本身造輪子。咱們看一下這個庫怎麼使用:
// use encoding/binary // bigEndian littleEndian func BigEndianAndLittleEndianByLibrary() { var value uint32 = 10 by := make([]byte,4) binary.BigEndian.PutUint32(by,value) fmt.Println("轉換成大端後 ",by) fmt.Println("使用大端字節序輸出結果:",binary.BigEndian.Uint32(by)) little := binary.LittleEndian.Uint32(by) fmt.Println("大端字節序使用小端輸出結果:",little) } // 結果: 轉換成大端後 [0 0 0 10] 使用大端字節序輸出結果: 10 大端字節序使用小端輸出結果: 167772160
grpc
中對大端的應用你們對gRPC
必定很熟悉,最近在看gRPC
源碼時,看到gRPC
封裝message
時,在封裝header
時,特地指定了使用大端字節序,源碼以下:
// msgHeader returns a 5-byte header for the message being transmitted and the // payload, which is compData if non-nil or data otherwise. func msgHeader(data, compData []byte) (hdr []byte, payload []byte) { hdr = make([]byte, headerLen) if compData != nil { hdr[0] = byte(compressionMade) data = compData } else { hdr[0] = byte(compressionNone) } // Write length of payload into buf binary.BigEndian.PutUint32(hdr[payloadLen:], uint32(len(data))) return hdr, data }
在本文的最後咱們再來作一下總結:
encoding/binary
;本文的全部代碼已經上傳github
:https://github.com/asong2020/...;歡迎star
好啦,這篇文章就到這裏啦,素質三連(分享、點贊、在看)都是筆者持續創做更多優質內容的動力!
建立了一個Golang學習交流羣,歡迎各位大佬們踊躍入羣,咱們一塊兒學習交流。入羣方式:加我vx拉你入羣,或者公衆號獲取入羣二維碼
結尾給你們發一個小福利吧,最近我在看[微服務架構設計模式]這一本書,講的很好,本身也收集了一本PDF,有須要的小夥能夠到自行下載。獲取方式:關注公衆號:[Golang夢工廠],後臺回覆:[微服務],便可獲取。
我翻譯了一份GIN中文文檔,會按期進行維護,有須要的小夥伴後臺回覆[gin]便可下載。
翻譯了一份Machinery中文文檔,會按期進行維護,有須要的小夥伴們後臺回覆[machinery]便可獲取。
我是asong,一名普普統統的程序猿,讓咱們一塊兒慢慢變強吧。歡迎各位的關注,咱們下期見~~~
推薦往期文章: