Go 語言中 Unsafe 包的用法

2021-10-25 02:28:45 字數 2946 閱讀 2829

- 後端早讀課翻譯計畫 第三篇-

- 翻譯自: a-journey-with-go

本文列舉在 golang 中 unsafe 的一般用法,以及提供給開發者的一些建議。

ℹ️ 本文基於 go 1.12 版本。

這個 package 的名字其實已經告訴了我們,我們不應該用它。為了弄清楚為什麼用它會導致不安全,我們先看下文件裡說的:

package unsafe 包含一些可以繞過型別安全的操作。

匯入 unsafe 的包可能是不可移植( non-portable) 的,而且不受 go 1 相容性準則的保護

因此,這個命名作為 go 中型別安全的對立面。讓我們深入**文件中提到的這兩點。

在 go 中,每個變數都有型別,並且在賦值給其他變數之前,可以被轉換成對應的型別。在這個轉換過程中, go 為了適應新的型別,會對資料進行轉換。比如:

var i int8 = -1 // -1 binary representation: 11111111

var j = int16(i) // -1 binary representation: 11111111 11111111

println(i, j) // -1 -1

var k uint8 = *(*uint8)(unsafe.pointer(&i))

println(k) // 255 is the uint8 value for the binary 11111111

原始值現在被解釋為 uint8,而不是用原來宣告的型別的值(也可以通過強制型別轉換把 int8 轉為 uint8 ——譯者注)。如果你對這個話題想做更深入的了解,我推薦你閱讀另外一篇文章 cast and conversion with go

go 1 的手冊清楚地說明,如果他們改變了實現方式,使用 unsafe 包可能會破壞你的**。

引入了 unsafe 的包可能依賴於 go 實現的內部屬性。我們保留修改實現方法的權利,這也許會破壞此類程式

我們需要記住的是,在 go 1 中,內部實現可能會發生變化,並且我們也許會遇到類似 issues this ticket in github 中所見的問題,兩個 go 版本之間的行為有略微的改變。不過, go 的一些標準庫中也在許多地方使用了 unsafe 包。

reflection 包是使用 unsafe 最多的包之一。reflection 基於空介面包含的內部資料。為了讀取資料, go 只是將我們的變數轉變為空介面,並通過對映乙個與空介面的內部表示形式匹配的結構和指標位址處的記憶體來讀取它們。

func valueof(i inte***ce{}) value 

// unpackeface converts the empty inte***ce i to a value.

func unpackeface(i inte***ce{}) value

變數e現在包含了關於該值的所有資訊,比如型別或值是否被匯出。反射也會用 unsafe 包來通過直接在記憶體中更新值來修改發射變數的值,就像我們之前已經看到的。

在 sync 包中,unsafe 有另乙個有趣的用法。如果你不熟悉 sync 包,我推薦閱讀 design of sync.pool

共享池(pooles) 通過一塊記憶體片段被所有 goroutines/processors 共享,所有的 goroutines 都可以通過 unsafe 包訪問它。

func indexlocal(l unsafe.pointer, i int) *poollocal ))

return (*poollocal)(lp)

}

變數l就是那塊記憶體片段, i 是 processor 編號。方法 indexlocal 只是讀取這個記憶體片段——包括了 x ( processor 的編號)poollocal 結構體——通過 index 的偏移量去讀取。儲存乙個指標指向整個記憶體片段是實現共享池的一種非常輕量的方式。

go 也在 runtime 裡面使用了很多次 unsafe 包,因為它需要處理記憶體操作,比如堆疊的分配或者釋放堆疊記憶體。堆疊的結構中由兩個邊界來表示:

type stack struct
然後, unsafe 包將會有助於進行以下操作:

func stackfree(stk stack)
如果你想更深入的了解堆疊,可以閱讀這篇文章 stack size and its management.

同時,我們可以在某些時候在我們的應用裡使用這個包,比如兩個 struct 之間的轉換。

unsafe 包的乙個優雅的用法就是用來轉換兩個基礎資料一樣的 struct,他們本來是不能被轉換器轉換的:

type a struct 

type b struct

func main()

//b := b(a) cannot convert a (type a) to type b

b := *(*b)(unsafe.pointer(&a))

println(b.d, b.e, b.f) // 1 foo 1.23

}

source:

另外乙個優雅的使用方式是在 ,它可以幫助你理解 struct 填充的大小。

總結來說,unsafe 包是乙個比較有趣而且強大的工具,應謹慎使用。另外,如果你想對該包的未來版本進行更新,可以在這裡找到建議 the proposals in github for go 2.

閱讀原文

go語言中閉包

閉包 closure 在一些語言中,在函式中可以 巢狀 定義另乙個函式時,如果內部的函式引用了外部的函式的變數,則可能產生閉包。閉包可以用來在乙個函式與一組 私有 變數之間建立關聯關係。在給定函式被多次呼叫的過程中,這些私有變數能夠保持其永續性。golang的閉包 函式在golang中是 一等公民 ...

GO語言中的閉包

package main import fmt import fmt func main r res fmt.println r 1 r2 res fmt.println r2 2 r3 res fmt.println r3 3 乙個外層函式中有內層函式,該內層函式中,會操作外層函式的區域性變數 外...

GO語言中的strconv包

在go語言中經常用到字串和其它型別進行轉換,strconv包能實現這個功能。有兩種方法,可以使用func atoi s string i int,err error atoi代表ascii to integer。還可以使用func parseint s string,base int,bitsize...