unsafe 包簡介
Go是一個記憶體安全的程式語言,c語言讓我們可以直接操控記憶體,好處是減去了中間處理記憶體的成本從而加速了程式,而Go也提供了一個unsafe包讓我們能直接操作記憶體。
unsafe包僅有三個函式及一個型別,函式分別為Sizeof(接收任何型態的變數,回傳該變數使用了多少bytes)、Offsetof(接收一個struct的一個欄位,回傳從struct開頭到那個欄位的bytes)、Alignof(接收一個欄位或變數,回傳該類型需要的bytes對齊數);一個型別是Pointer,注意unsafe.Pointer 與uintptr不是同一種型別,但可以互相轉換。
既然包的名稱都叫unsafe了,通常情況下我們不會在程式中使用它,unsafe包的使用時機通常是為了與其他系統的互通性,例如和操作系統(OS)的緊密配合、或是提高性能,下述的用unsafe轉換二進制資料的例子即可以提高程式的性能。但是它是不安全的,可能因程式的bug產生奇怪的錯誤,同時也不保證與未來版本的兼容性。
二進制的轉換程式
這是一個處理網路傳來的封包的程式,依據某一個protocal(定義如下面的DataPackage),我們先來直接看程式,下面再說明:
(網路讀取的資料例如: [ 7 182 93 89 212 111 110 93 21 0 0 0 0 0 1 0] 共 16 bytes
type DataPackage struct { Value uint32 //4 bytes Title [10]byte //10 bytes Enabled bool //1 byte } //使用golang其他安全的包來處理 func DataConvert(b [16]byte) DataPackage { result := DataPackage{} result.Value = binary.BigEndian.Uint32(b[:4}) //將大端序的bytes資料存成系統中定義的uint32資料 copy(result.Title[:],b[4:14]) result.Enabled = b[14] != 0 //從byte轉成 true or false return result } //使用unsafe 包來處理 func DataConvertUnsafe(b [16]byte) DataPackage { result :=*(*DataPackage)(unsafe.Pointer(&b)) if isLittleEndian { result.Value = bits.ReverseBytes32(result.Value) } return result } //-------------------------------------------------------------- //以下用來判斷電腦是大端序還是小端序編碼 var isLittleEndian bool func init(){ var x uint16 = 0xFF00 xbyte := *(*[2]byte)(unsafe.Pointer(&x)) isLittleEndian = (xbyte[0] == 0x00) }
程式說明
- 其實主要需要說明的部分在於這一句 : *(*DataPackage)(unsafe.Pointer(&b));我們要從後面說明起:
- &b: 將b這個變數(為一個16個byte的array)的指標(uintptr型態) 列出來
- unsafe.Pointer(unitptr)會將unitptr指標轉換成 unsafe.Pointer指標這種型態,讓我們可以對記憶體進行直接操作
- (*DataPackage)unsafe.Pointer則是將一個Pointer指標直接轉成 DataPackage這種型態的指標(利用* 來轉成指標)
- 最後再用* 將轉換成的指標轉回原本的資料內容,也就是DataPackage type的資料
- 相較於不使用unsafe包的程式,需要每個資料的存到result中,使用unsafe包的程式利用記憶體轉換直接完成將資料存到result中的目的。速度在某些機器上可快兩倍 (不同機器需要實測)。
- 網路上傳輸的資料通常都是大端序的,而電腦上則會有小端序或大端序的不同,以一個值為 0xFF00的數字x,在大端序中記憶體會存為[FF 00],小端序則存為[00 FF],因此我們利用*(*[2]byte)(unsafe.Pointer(&x))將x強制轉成一個2 bytes的array, 觀察其[0] 的數字是否為00來判斷系統是否為小端序。
- 註: init()函數為Package被引入時會被自動執行的函數
雖然上述例子有帶來更快的程式的好處,但考量到程式的容易閱讀性及可能的不安全性,除非真的很有效能的考量,否則還真的不建議使用unsafe呢。