如何利用unsafe包修改私有成員

對於一個結構體,通過 offset 函數可以獲取結構體成員的偏移量,進而獲取成員的地址,讀寫該地址的內存,就可以達到改變成員值的目的。

這裏有一個內存分配相關的事實:結構體會被分配一塊連續的內存,結構體的地址也代表了第一個成員的地址。

我們來看一個例子:

package main

import (
	"fmt"
	"unsafe"
)

type Programmer struct {
	name string
	language string
}

func main() {
	p := Programmer{"stefno", "go"}
	fmt.Println(p)
	
	name := (*string)(unsafe.Pointer(&p))
	*name = "qcrao"

	lang := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.language)))
	*lang = "Golang"

	fmt.Println(p)
}

運行代碼,輸出:

{stefno go}
{qcrao Golang}

name 是結構體的第一個成員,因此可以直接將 &p 解析成 *string。這一點,在前面獲取 map 的 count 成員時,用的是同樣的原理。

對於結構體的私有成員,現在有辦法可以通過 unsafe.Pointer 改變它的值了。

我把 Programmer 結構體升級,多加一個字段:

type Programmer struct {
	name string
	age int
	language string
}

並且放在其他包,這樣在 main 函數中,它的三個字段都是私有成員變量,不能直接修改。但我通過 unsafe.Sizeof() 函數可以獲取成員大小,進而計算出成員的地址,直接修改內存。

func main() {
	p := Programmer{"stefno", 18, "go"}
	fmt.Println(p)

	lang := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Sizeof(int(0)) + unsafe.Sizeof(string(""))))
	*lang = "Golang"

	fmt.Println(p)
}

輸出:

{stefno 18 go}
{stefno 18 Golang}

轉載出處

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