解讀unsafe.Pointer和uintptr的區(qū)別
unsafe 包
func Alignof(x ArbitraryType) uintptr func Offsetof(x ArbitraryType) uintptr func Sizeof(x ArbitraryType) uintptr type ArbitraryType int type Pointer *ArbitraryType
在unsafe包中,只提供了3個函數(shù),兩個類型。就這么少的量,卻有著超級強(qiáng)悍的功能。
ArbitraryType
// ArbitraryType is here for the purposes of documentation only and is not actually // part of the unsafe package. It represents the type of an arbitrary Goexpression. // ArbitryType僅用于文檔目的,實際上并非不安全包的一部分。它表示任意Go表達(dá)式的類型。 type ArbitraryType int
ArbitraryType 是以int為基礎(chǔ)定義的一個新類型,但是Go 語言unsafe包中,對ArbitraryType賦予了特殊的意義,通常,把interface{}看作是任意類型,那么ArbitraryType這個類型,在Go 語言系統(tǒng)中,比interface{}還要隨意。
Pointer
Pointer 是ArbitraryType指針類型為基礎(chǔ)的新類型,在Go 語言系統(tǒng)中,可以把Pointer類型,理解成任何指針的親爹。
Go 語言的指針類型長度與int類型長度,在內(nèi)存中占用的字節(jié)數(shù)是一樣的。ArbitraryType類型的變量也可以是指針。
// Alignof返回變量對齊字節(jié)數(shù)量 func Alignof(x ArbitraryType) uintptr // Offsetof返回變量指定屬性的偏移量,所以如果變量是一個struct類型,不能直接將這個struct類型的變量當(dāng)作參數(shù),只能將這個struct類型變量的屬性當(dāng)作參數(shù)。 func Offsetof(x ArbitraryType) uintptr // Sizeof 返回變量在內(nèi)存中占用的字節(jié)數(shù),切記,如果是slice,則不會返回這個slice在內(nèi)存中的實際占用長度。 func Sizeof(x ArbitraryType) uintptr
unsafe中,通過ArbitraryType 、Pointer 這兩個類型,可以將其他類型都轉(zhuǎn)換過來,然后通過這三個函數(shù),分別能取長度,偏移量,對齊字節(jié)數(shù),就可以在虛擬內(nèi)存中來回調(diào)度。
指針運算
- uintptr這個基礎(chǔ)類型,在Go 語言中,字節(jié)長度是與int一致。
- 通常Pointer不能參與指針運算,比如要在某個指針地址上加上一個偏移量,Pointer是不能做這個運算的
- 只有將Pointer類型先轉(zhuǎn)換成uintptr類型,做完地址加減法運算后,再轉(zhuǎn)換成Pointer類型,通過*操作達(dá)到取值、修改值的目的。
- unsafe.Pointer其實就是類似C的void *,在Go 語言中是用于各種指針相互轉(zhuǎn)換的橋梁,也即是通用指針。它可以讓任意類型的指針實現(xiàn)相互轉(zhuǎn)換,也可以將任意類型的指針轉(zhuǎn)換為 uintptr 進(jìn)行指針運算。
- uintptr是Go 語言的內(nèi)置類型,是能存儲指針的整型, uintptr 的底層類型是int,它和unsafe.Pointer可相互轉(zhuǎn)換。
unsafe.Pointer和uintptr的區(qū)別
- unsafe.Pointer只是單純的通用指針類型,用于轉(zhuǎn)換不同類型指針,它不可以參與指針運算;
- 而uintptr是用于指針運算的,GC 不把 uintptr 當(dāng)指針,也就是說 uintptr 無法持有對象, uintptr 類型的目標(biāo)會被回收;
- unsafe.Pointer 可以和 普通指針 進(jìn)行相互轉(zhuǎn)換;
- unsafe.Pointer 可以和 uintptr 進(jìn)行相互轉(zhuǎn)換。
unsafe包簡單使用
準(zhǔn)備結(jié)構(gòu)體,成員不導(dǎo)出

初始化結(jié)構(gòu)體
func main() {
s:=pkg.UnsafeStruct{}
// {0 0}
fmt.Println(s)
}
眾所周知,結(jié)構(gòu)體的地址就是第一個成員的地址
func main() {
s:=pkg.UnsafeStruct{}
// 取成員1
field1Pointer:=unsafe.Pointer(&s)
fmt.Println(field1Pointer)
// 轉(zhuǎn)為int32類型指針
field1Ptr:=(*int32)(field1Pointer)
fmt.Println(*field1Ptr)
}

賦值,可以看到私有字段已經(jīng)被改變
func main() {
s:=pkg.UnsafeStruct{}
// 取成員1
field1Pointer:=unsafe.Pointer(&s)
fmt.Println(field1Pointer)
// 轉(zhuǎn)為int32類型指針
field1Ptr:=(*int32)(field1Pointer)
fmt.Println(*field1Ptr)
// 賦值
*field1Ptr = 10
fmt.Println(s)
}

利用偏移量改變字段2的值
func main() {
s:=pkg.UnsafeStruct{}
// 取成員1
field1Pointer:=unsafe.Pointer(&s)
fmt.Println(field1Pointer)
// 轉(zhuǎn)為int32類型指針
field1Ptr:=(*int32)(field1Pointer)
fmt.Println(*field1Ptr)
// 賦值
*field1Ptr = 1314
fmt.Println(s)
// 獲取成員2的Pointer
filed2Pointer:= unsafe.Pointer(uintptr(field1Pointer)+ unsafe.Sizeof(int64(0)))
fmt.Println(filed2Pointer)
// 轉(zhuǎn)為int64類型指針
field2Ptr:=(*int64)(filed2Pointer)
fmt.Println(*field2Ptr)
// 賦值
*field2Ptr = 520
fmt.Println(s)
}

成員聲明為int32和int64是為了避免對齊的影響,否則就要加上對齊值
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
一文帶你了解Golang中強(qiáng)大的重試機(jī)制
在 Go 語言中,處理瞬態(tài)錯誤是常見的挑戰(zhàn),這些錯誤可能會在一段時間后自動恢復(fù),因此,重試機(jī)制在這些情況下非常重要,所以本文就來和大家聊聊Golang中強(qiáng)大的重試機(jī)制吧2025-01-01
獲取Golang環(huán)境變量的三種方式小結(jié)
本文介紹了Golang中獲取環(huán)境變量的三種方式,包含使用Viper包、GoDotEnv包和os包,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-11-11
Golang?throttled基于GCRA速率限制庫使用探索
這篇文章主要為大家介紹了Golang?throttled基于GCRA速率限制庫使用實例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
如何將Golang數(shù)組slice轉(zhuǎn)為逗號分隔的string字符串
這篇文章主要介紹了如何將Golang數(shù)組slice轉(zhuǎn)為逗號分隔的string字符串問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09
GoLang并發(fā)機(jī)制探究goroutine原理詳細(xì)講解
goroutine是Go語言提供的語言級別的輕量級線程,在我們需要使用并發(fā)時,我們只需要通過 go 關(guān)鍵字來開啟 goroutine 即可。這篇文章主要介紹了GoLang并發(fā)機(jī)制goroutine原理,感興趣的可以了解一下2022-12-12
Go創(chuàng)建一個包并使用(導(dǎo)入本地包和注意事項)
有時候需要自己寫一個包方便多次使用,但是在導(dǎo)入自己寫的包時遇到了問題,本文主要介紹了Go創(chuàng)建一個包并使用(導(dǎo)入本地包和注意事項),感興趣的可以了解一下2023-11-11
GoZero實現(xiàn)數(shù)據(jù)庫MySQL單例模式連接的簡單示例
在 GoZero 框架中實現(xiàn)數(shù)據(jù)庫的單例連接可以通過以下步驟來完成,GoZero 使用 gorm 作為默認(rèn)的數(shù)據(jù)庫操作框架,接下來我會展示一個簡單的單例模式實現(xiàn),需要的朋友可以參考下2025-02-02

