Go中非類型安全unsafe包的詳細使用
一、Go中的 unsafe 概述
1.1 什么是unsafe?
Go 語言中的 unsafe 包是一個既強大又危險的工具,它允許我們繞過 Go 的類型系統(tǒng),直接操作內存。雖然它在某些高性能場景下非常有用,但使用不當也會導致程序崩潰或安全漏洞。
unsafe 是 Go 語言中的一個特殊包,它提供了一些可以繞過 Go 類型安全機制的機制。通過 unsafe,你可以:
- 獲取變量的內存地址
- 直接讀寫內存
- 將任意類型轉換為
uintptr(指針的整數(shù)表示) - 訪問結構體的私有字段
?? 注意:使用 unsafe 會破壞 Go 的類型安全和內存安全,應謹慎使用,并盡量避免在生產代碼中濫用。
1.2 使用unsafe的注意事項
- 不保證兼容性:
unsafe的實現(xiàn)可能隨 Go 版本變化,代碼可能在新版本中失效。 - GC 無法追蹤
uintptr:如果將uintptr轉換為unsafe.Pointer后沒有立即使用,可能會被 GC 回收,導致非法訪問。 - 類型安全被破壞:可能導致內存損壞、數(shù)據競爭或程序崩潰。
- 可讀性差:濫用
unsafe會讓代碼難以理解和維護。
1.3 指針轉換規(guī)則
Go 語言中存在三種類型的指針,它們分別是:常用的 *T、unsafe.Pointer 及 uintptr??梢钥偨Y出這三者的轉換規(guī)則:
- 任何類型的 *T 都可以轉換為 unsafe.Pointer;
- unsafe.Pointer 也可以轉換為任何類型的 *T;unsafe.Pointer 可以轉換為 uintptr;
- uintptr 也可以轉換為 unsafe.Pointer。

可以發(fā)現(xiàn),unsafe.Pointer 主要用于指針類型的轉換,而且是各個指針類型轉換的橋梁。
二、unsafe的核心內容
2.1unsafe.Pointer
unsafe.Pointer 是一種特殊的指針類型,它可以指向任意類型的數(shù)據。它和普通指針(如 *int)之間的主要區(qū)別是:
- 普通指針不能隨意轉換類型
unsafe.Pointer可以和uintptr互相轉換,從而實現(xiàn)指針運算
var x int = 42 p := unsafe.Pointer(&x) // &x 是 *int 類型,可以轉換為 unsafe.Pointer
2.2uintptr
uintptr 是一個整數(shù)類型,足夠大以存儲任意指針的值。它常用于指針運算,例如:
p := unsafe.Pointer(&x) ptr := uintptr(p) // 轉換為 uintptr ptr += 8 // 指針運算 p = unsafe.Pointer(ptr) // 再轉回 unsafe.Pointer
?? 注意:uintptr 不是指針,它不會被 GC 追蹤,因此不能長時間持有。
2.3unsafe.Sizeof、unsafe.Alignof、unsafe.Offsetof
1、unsafe.Sizeof
Sizeof:返回類型或變量的大?。ㄗ止?jié))。Sizeof 函數(shù)可以返回一個類型所占用的內存大小,這個大小只與類型有關,和類型對應的變量存儲的內容大小無關,比如 bool 型占用一個字節(jié)、int8 也占用一個字節(jié)。
通過 Sizeof 函數(shù)你可以查看任何類型(比如字符串、切片、整型)占用的內存大小,示例代碼如下:
fmt.Println(unsafe.Sizeof(true))
fmt.Println(unsafe.Sizeof(int8(0)))
fmt.Println(unsafe.Sizeof(int16(10)))
fmt.Println(unsafe.Sizeof(int32(10000000)))
fmt.Println(unsafe.Sizeof(int64(10000000000000)))
fmt.Println(unsafe.Sizeof(int(10000000000000000)))
fmt.Println(unsafe.Sizeof(string("數(shù)據知道")))
fmt.Println(unsafe.Sizeof([]string{"數(shù)據u知道","張三"}))
對于整型來說,占用的字節(jié)數(shù)意味著這個類型存儲數(shù)字范圍的大小,比如 int8 占用一個字節(jié),也就是 8bit,所以它可以存儲的大小范圍是 -128~~127,也就是 −2^(n-1) 到 2^(n-1)−1。其中 n 表示 bit,int8 表示 8bit,int16 表示 16bit,以此類推。
小提示:一個 struct 結構體的內存占用大小,等于它包含的字段類型內存占用大小之和。
2、Alignof
Alignof:是 Go 語言 unsafe 包中的一個函數(shù),用于返回某個類型的對齊系數(shù)(alignment),即該類型的變量在內存中存放時的起始地址必須是其對齊系數(shù)的整數(shù)倍。
func Alignof(x ArbitraryType) uintptr
- 參數(shù):x 可以是任意類型的表達式(通常傳遞一個變量或零值)。
- 返回值:uintptr,表示該類型的對齊系數(shù)(單位是字節(jié))。
3、Offsetof
Offsetof:返回結構體字段相對于結構體起始地址的偏移量
三、案例分析
3.1 案例 1:使用unsafe修改結構體私有字段
3.2 案例 2:指針運算模擬數(shù)組訪問
package main
import (
"fmt"
"unsafe"
)
func main() {
arr := [3]int{10, 20, 30}
// 獲取數(shù)組首地址
basePtr := unsafe.Pointer(&arr[0])
// 模擬指針運算訪問第二個元素
secondPtr := (*int)(unsafe.Pointer(uintptr(basePtr) + unsafe.Sizeof(arr[0])))
fmt.Println(*secondPtr) // 輸出 20
}
3.3 案例 3:string與[]byte的零拷貝轉換
package main
import (
"fmt"
"unsafe"
)
func main() {
s := "hello, unsafe"
// 獲取 string 的底層結構
strHeader := (*struct {
data uintptr
len int
})(unsafe.Pointer(&s))
// 構造 []byte 的底層結構
bytes := *(*[]byte)(unsafe.Pointer(&struct {
data uintptr
len int
cap int
}{
data: strHeader.data,
len: strHeader.len,
cap: strHeader.len,
}))
fmt.Println(string(bytes)) // 輸出 "hello, unsafe"
}
總結:unsafe 是 Go 語言中的一把“雙刃劍”。unsafe 包里的功能雖然不安全,但的確很香,比如指針運算、類型轉換等,都可以幫助我們提高性能。不過還是建議盡可能地不使用,因為它可以繞開 Go 語言編譯器的檢查,可能會因為你的操作失誤而出現(xiàn)問題。當然如果是需要提高性能的必要操作,還是可以使用,比如 []byte 轉 string,就可以通過 unsafe.Pointer 實現(xiàn)零內存拷貝,
到此這篇關于Go中非類型安全unsafe包的詳細使用的文章就介紹到這了,更多相關Go 非類型安全unsafe包內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go實現(xiàn)用戶每日限額的方法(例一天只能領三次福利)
這篇文章主要介紹了Go實現(xiàn)用戶每日限額的方法(例一天只能領三次福利)2022-01-01
Golang小數(shù)操作指南之判斷小數(shù)點位數(shù)與四舍五入
這篇文章主要給大家介紹了關于Golang小數(shù)操作指南之判斷小數(shù)點位數(shù)與四舍五入的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-03-03

