go unsafe 包

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。

  1. 任何類型的 *T 都可以轉換爲 unsafe.Pointer;
  2. unsafe.Pointer 也可以轉換爲任何類型的 *T;
  3. unsafe.Pointer 可以轉換爲 uintptr;
  4. 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 位,會取最大的

參考

22 講通關 Go 語言

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章