unsafe包
unsafe包配合uintptr可以繞過(guò)go的安全檢查邻吞,對(duì)內(nèi)存進(jìn)行操作
func Alignof(x ArbitraryType) uintptr
func Offsetof(x ArbitraryType) uintptr
func Sizeof(x ArbitraryType) uintptr
type ArbitraryType int
type Pointer *ArbitraryType
unsafe包中提供3個(gè)函數(shù),2個(gè)類型
兩個(gè)類型
ArbitraryType
ArbitraryType
類型,底層定義是int類型层玲,但是go賦予了特殊的意義界轩,我們完全可以無(wú)視該類型,當(dāng)做占位符來(lái)看玩荠,因?yàn)樗?code>interface{}還要隨意
Pointer
Pointer
類型漆腌,底層定義是ArbitraryType
的指針類型,可以認(rèn)為是通用指針類型姨蟋,記住兩個(gè)規(guī)則即可
-
Pointer
可以和普通指針進(jìn)行相互轉(zhuǎn)換 -
Pointer
可以和uintptr
進(jìn)行相互轉(zhuǎn)換
unitptr
// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr
uintptr
是自定義的內(nèi)置類型屉凯,翻譯過(guò)來(lái)的意思就是uintptr
是一個(gè)整數(shù)類型,且能容納任何指針
通過(guò)uintptr
眼溶,我們就能進(jìn)行指針運(yùn)算悠砚,進(jìn)而訪問(wèn)內(nèi)存甚至修改內(nèi)存數(shù)據(jù)
Pointer和unitptr的區(qū)別
-
Pointer
只是單純的通用指針類型,用于轉(zhuǎn)換不同類型指針堂飞,它不可以參與指針運(yùn)算 -
uintptr
是用于指針運(yùn)算的灌旧,GC
不把uintptr
當(dāng)指針,也就是說(shuō)uintptr
無(wú)法持有對(duì)象绰筛,也就無(wú)法阻止GC
回收uintptr
對(duì)應(yīng)的內(nèi)存地址的內(nèi)存數(shù)據(jù)
三個(gè)函數(shù)
Offsetof函數(shù)
Offsetof
方法主要作用是返回結(jié)構(gòu)體成員在內(nèi)存中的位置離結(jié)構(gòu)體起始處(結(jié)構(gòu)體的第一個(gè)字段的偏移量都是0)的字節(jié)數(shù)枢泰,即偏移量,通過(guò)源碼注釋可知其入?yún)⒈仨毷且粋€(gè)結(jié)構(gòu)體
v := &V{1, 2}
// Pointer與任何指針類型都和互相轉(zhuǎn)換
// 結(jié)構(gòu)體的第一個(gè)字段的偏移量都是0铝噩,這里vp就是指向v.i的指針
vp := unsafe.Pointer(v)
vp1 := (*int32)(vp)
fmt.Println(*vp1)
// 更改同步v.i
*vp1 = 3
fmt.Println(v.i)
// 通過(guò)Offsetof獲取v.j的相對(duì)偏移量
// uintptr可進(jìn)行指針運(yùn)算衡蚂,即v的初始內(nèi)存地址 + v.j的內(nèi)存地址相對(duì)偏移量 = v.j的地址
// 也可通過(guò)Sizeof獲取int64類型的內(nèi)存占用字節(jié)數(shù)來(lái)計(jì)算v.j的地址,不要以為v.i是int32類型的就使用int32骏庸,還要注意結(jié)構(gòu)體的內(nèi)存對(duì)齊
vp2 := (*int64)(unsafe.Pointer(uintptr(vp) + unsafe.Offsetof(v.j)))
vp3 := (*int64)(unsafe.Pointer(uintptr(vp) + unsafe.Sizeof(int64(0))))
vp4 := (*int64)(unsafe.Pointer(uintptr(vp) + unsafe.Sizeof(int32(0))))
fmt.Println(*vp2, *vp3, *vp4)
// output
1
3
2 2 8589934592
Sizeof函數(shù)
Sizeof
返回變量在內(nèi)存中占用的字節(jié)數(shù)(切記毛甲,如果是slice,則不會(huì)返回這個(gè)slice在內(nèi)存中的實(shí)際占用長(zhǎng)度)
a := [3]int{1, 2, 3}
// Pointer與任何指針類型都和互相轉(zhuǎn)換
ap := unsafe.Pointer(&a)
ap1 := (*int)(ap)
fmt.Println(*ap1)
// 更改同步影響a[0]
*ap1 = 4
fmt.Println(a)
// Sizeof獲取int類型占用的字節(jié)數(shù)
// uintptr可進(jìn)行指針運(yùn)算具被,即a的初始內(nèi)存地址 + 1個(gè)int類型占用的字節(jié)數(shù) = a[1]的地址
// 這里int(0)和a[2]結(jié)果是一模一樣的玻募,同理a[0],a[1]
ap2 := (*int)(unsafe.Pointer(uintptr(ap) + unsafe.Sizeof(int(0))))
ap3 := (*int)(unsafe.Pointer(uintptr(ap) + unsafe.Sizeof(a[2])))
fmt.Println(*ap2, *ap3)
// 全部輸出24,即a占用的字節(jié)數(shù)
s := a[:1]
s1 := a[1:1]
s2 := a[1:2]
fmt.Println(unsafe.Sizeof(s), unsafe.Sizeof(s1), unsafe.Sizeof(s2))
// output
1
[4 2 3]
2 2
24 24 24
Alignof函數(shù)
請(qǐng)先看Go結(jié)構(gòu)體的內(nèi)存對(duì)齊
Alignof
用于獲取變量的對(duì)齊系數(shù)一姿,獲取到變量的對(duì)齊系數(shù)后七咧,就可以計(jì)算出結(jié)構(gòu)體中字段的偏移量(對(duì)齊系數(shù)的倍數(shù))