unsafe包提供了訪問底層內(nèi)存的方法。是用unsafe函數(shù)可以提高訪問對(duì)象的速度慕趴。通常用于對(duì)大數(shù)組的遍歷痪蝇。
unsafe內(nèi)容介紹
func Alignof(x ArbitraryType) uintptr
func Offsetof(x ArbitraryType) uintptr
func Sizeof(x ArbitraryType) uintptr
type ArbitraryType int
type Pointer *ArbitraryType
- 通過指針加偏移量的操作鄙陡,在地址中,修改霹俺,訪問變量的值
這個(gè)包中柔吼,只提供了3個(gè)函數(shù),兩個(gè)類型
unsafe中丙唧,通過這兩個(gè)個(gè)兼容萬物的類型,將其他類型都轉(zhuǎn)換過來觅玻,然后通過這三個(gè)函數(shù),分別能取長(zhǎng)度溪厘,偏移量畸悬,對(duì)齊字節(jié)數(shù),就可以在內(nèi)存地址映射中披粟,來回游走守屉。放在c語言中拇泛,是不是思灌,只要給你一個(gè)起始地址泰偿,你就一下子干到底L鹧佟!牍氛!在golang中搬俊,通過unsafe包,你也可以盡情的去放縱
uintptr:用于指針運(yùn)算餐屎,GC 不把 uintptr 當(dāng)指針腹缩,uintptr 無法持有對(duì)象藏鹊。uintptr 類型的目標(biāo)會(huì)被回收盘寡。
unsafe.Pointer 可以和 普通指針 進(jìn)行相互轉(zhuǎn)換撮慨。
unsafe.Pointer 可以和 uintptr 進(jìn)行相互轉(zhuǎn)換砌溺。
也就是說 unsafe.Pointer 是橋梁抚吠,可以讓任意類型的指針實(shí)現(xiàn)相互轉(zhuǎn)換楷力,也可以將任意類型的指針轉(zhuǎn)換為 uintptr 進(jìn)行指針運(yùn)算萧朝。
詳細(xì)說明
- type ArbitraryType int
是int的一個(gè)別名检柬,但是golang中何址,對(duì)ArbitraryType賦予了特殊的意義,
- type Pointer *ArbitraryType
是int指針類型的一個(gè)別名原押,在golang系統(tǒng)中诸衔,可以把Pointer類型笨农,理解成任何指針的親爹。
- func Alignof(x ArbitraryType) uintptr
Alignof返回變量對(duì)齊字節(jié)數(shù)量
- func Offsetof(x ArbitraryType) uintptr
Offsetof返回變量指定屬性的偏移量雨膨,這個(gè)函數(shù)雖然接收的是任何類型的變量幢泼,但是這個(gè)又一個(gè)前提脾还,就是變量要是一個(gè)struct類型鄙漏,且還不能直接將這個(gè)struct類型的變量當(dāng)作參數(shù)怔蚌,只能將這個(gè)struct類型變量的屬性當(dāng)作參數(shù)旁赊。
- func Sizeof(x ArbitraryType) uintptr
Sizeof 返回變量在內(nèi)存中占用的字節(jié)數(shù)籍胯,切記离福,如果是slice妖爷,則不會(huì)返回這個(gè)slice在內(nèi)存中的實(shí)際占用長(zhǎng)度絮识。
示例
通過指針修改結(jié)構(gòu)體字段
package main
import (
"fmt"
"unsafe"
)
func main() {
s := struct {
a byte
b byte
c byte
d int64
}{0, 0, 0, 0}
// 將結(jié)構(gòu)體指針轉(zhuǎn)換為通用指針
p := unsafe.Pointer(&s)
// 保存結(jié)構(gòu)體的地址備用(偏移量為 0)
up0 := uintptr(p)
// 將通用指針轉(zhuǎn)換為 byte 型指針
pb := (*byte)(p)
// 給轉(zhuǎn)換后的指針賦值
*pb = 10
// 結(jié)構(gòu)體內(nèi)容跟著改變
fmt.Println(s)
// 偏移到第 2 個(gè)字段
up := up0 + unsafe.Offsetof(s.b)
// 將偏移后的地址轉(zhuǎn)換為通用指針
p = unsafe.Pointer(up)
// 將通用指針轉(zhuǎn)換為 byte 型指針
pb = (*byte)(p)
// 給轉(zhuǎn)換后的指針賦值
*pb = 20
// 結(jié)構(gòu)體內(nèi)容跟著改變
fmt.Println(s)
// 偏移到第 3 個(gè)字段
up = up0 + unsafe.Offsetof(s.c)
// 將偏移后的地址轉(zhuǎn)換為通用指針
p = unsafe.Pointer(up)
// 將通用指針轉(zhuǎn)換為 byte 型指針
pb = (*byte)(p)
// 給轉(zhuǎn)換后的指針賦值
*pb = 30
// 結(jié)構(gòu)體內(nèi)容跟著改變
fmt.Println(s)
// 偏移到第 4 個(gè)字段
up = up0 + unsafe.Offsetof(s.d)
// 將偏移后的地址轉(zhuǎn)換為通用指針
p = unsafe.Pointer(up)
// 將通用指針轉(zhuǎn)換為 int64 型指針
pi := (*int64)(p)
// 給轉(zhuǎn)換后的指針賦值
*pi = 40
// 結(jié)構(gòu)體內(nèi)容跟著改變
fmt.Println(s)
}
訪問數(shù)組
package main
import (
"fmt"
"unsafe"
)
type Foo struct {
A int
B int
}
func main() {
foo := &Foo{1, 2}
fmt.Println(foo)
base := uintptr(unsafe.Pointer(foo))
offset := unsafe.Offsetof(foo.A)
ptr := unsafe.Pointer(base + offset)
*(*int)(ptr) = 3
fmt.Println(foo)
}
修改其它包中的結(jié)構(gòu)體私有字段
方法A(指針遍歷)
package main
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
func main() {
// 創(chuàng)建一個(gè) strings 包中的 Reader 對(duì)象
// 它有三個(gè)私有字段:s string、i int64鲜屏、prevRune int
sr := strings.NewReader("abcdef")
// 此時(shí) sr 中的成員是無法修改的
fmt.Println(sr)
// 但是我們可以通過 unsafe 來進(jìn)行修改
// 先將其轉(zhuǎn)換為通用指針
p := unsafe.Pointer(sr)
// 獲取結(jié)構(gòu)體地址
up0 := uintptr(p)
// 確定要修改的字段(這里不能用 unsafe.Offsetof 獲取偏移量洛史,因?yàn)槭撬接凶侄危? if sf, ok := reflect.TypeOf(*sr).FieldByName("i"); ok {
// 偏移到指定字段的地址
up := up0 + sf.Offset
// 轉(zhuǎn)換為通用指針
p = unsafe.Pointer(up)
// 轉(zhuǎn)換為相應(yīng)類型的指針
pi := (*int64)(p)
// 對(duì)指針?biāo)赶虻膬?nèi)容進(jìn)行修改
*pi = 3 // 修改索引
}
// 看看修改結(jié)果
fmt.Println(sr)
// 看看讀出的是什么
b, err := sr.ReadByte()
fmt.Printf("%c, %v\n", b, err)
}
方法B(類型轉(zhuǎn)換)
// 定義一個(gè)和 strings 包中的 Reader 相同的本地結(jié)構(gòu)體
type Reader struct {
s string
i int64
prevRune int
}
func main() {
// 創(chuàng)建一個(gè) strings 包中的 Reader 對(duì)象
sr := strings.NewReader("abcdef")
// 此時(shí) sr 中的成員是無法修改的
fmt.Println(sr)
// 我們可以通過 unsafe 來進(jìn)行修改
// 先將其轉(zhuǎn)換為通用指針
p := unsafe.Pointer(sr)
// 再轉(zhuǎn)換為本地 Reader 結(jié)構(gòu)體
pR := (*Reader)(p)
// 這樣就可以自由修改 sr 中的私有成員了
(*pR).i = 3 // 修改索引
// 看看修改結(jié)果
fmt.Println(sr)
// 看看讀出的是什么
b, err := sr.ReadByte()
fmt.Printf("%c, %v\n", b, err)
}