go unsafe 包
unsafe包是不安全的,可以繞過go內存安全機制,直接對內存進行讀寫。
指針轉換
go 語言是強類型的,所以一般情況不允許不同類型指針進行轉換
func main() {
i:= 10
ip:=&i
var fp *float64 = (*float64)(ip)
fmt.Println(fp)
}
// cannot convert ip (type *int) to type *float64
如果需要轉換的時候,需要用的unsafe.Pointer
unsafe.Pointer
unsafe.Pointer 是一種特殊意義的指針,可以表示任意類型的地址
我們使用unsafe.Pointer,就可以將int指針改爲float64的指針並進行運算,下面是3倍乘法運算。
func main() {
i:= 10
ip:=&i
var fp *float64 = (*float64)(unsafe.Pointer(ip))
*fp = *fp * 3
fmt.Println(i) // 30
}
// 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 Go expression.
type ArbitraryType int
type Pointer *ArbitraryType
ArbitraryType 可以表示任何類型, 而 unsafe.Pointer 又是 *ArbitraryType,也就是說 unsafe.Pointer 是任何類型的指針,也就是一個通用型的指針。
unsafe.Pointer可以表示指針了,但是 unsafe.Pointer 不能進行運算,比如不支持 +(加號)運算符操作,
這個時候就需要用到 uintptr
uintptr
在 builtin/builtin.go 包中,是一種類型
// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr
通過uintptr,可以對指針偏移進行計算, 這樣就可以像c語言一樣訪問內存,對內存進行讀寫,真正操作內存。
func main() {
type person struct {
name string
age int
}
p := new(person)
pName := (*string)(unsafe.Pointer(p)) // 偏移量爲0
*pName = "test"
pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(p))+unsafe.Offsetof(p.age))) // 指針偏移
*pAge = 20
fmt.Println(p) //&{test 20}
}
對一個結構體進行內存操作賦值。
age 字段不是 person 的第一個字段,要修改它必須要進行指針偏移運算。所以需要先把指針變量 p 通過 unsafe.Pointer 轉換爲 uintptr, 這樣才能進行地址運算.這個偏移量可以通過函數 unsafe.Offsetof 計算出來,該函數返回的是一個 uintptr 類型的偏移量, 有了這個偏移量就可以通過 + 號運算符獲得正確的 age 字段的內存地址了,也就是通過 unsafe.Pointer 轉換後的 *int 類型的指針變量 pAge。
注意:
如果要進行指針運算,要先通過 unsafe.Pointer 轉換爲 uintptr 類型的指針。 指針運算完畢後,還要通過 unsafe.Pointer 轉換爲真實的指針類型(比如示例中的 *int 類型), 這樣可以對這塊內存進行賦值或取值操作。
指針運算的核心在於它操作的是一個個內存地址,通過內存地址的增減,就可以指向一塊塊不同的內存並對其進行操作,而且不必知道這塊內存被起了什麼名字(變量名)
指針轉換規則
Go 語言中存在三種類型的指針: 常用的 *T、unsafe.Pointer 及 uintptr。
- 任何類型的 *T 都可以轉換爲 unsafe.Pointer;
- unsafe.Pointer 也可以轉換爲任何類型的 *T;
- unsafe.Pointer 可以轉換爲 uintptr;
- uintptr 也可以轉換爲 unsafe.Pointer。
unsafe.Sizeof
Sizeof 函數可以返回一個類型所佔用的內存大小,這個大小隻與類型有關, 和類型對應的變量存儲的內容大小無關,比如 bool 型佔用一個字節、int8 也佔用一個字節。
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)))
對於和平臺有關的 int 類型,要看平臺是 32 位還是 64 位,會取最大的