golang使用unsafe包實現指針運算操做private變量

golang中是有指針概念的,可是go中的指針功能被限制了,不像C/C++中那樣,能夠對指針進行運算golang

好比在C/C++中 *p++ 這樣是正確的。可是在go中,這樣寫是錯誤的。至於go爲何會屏蔽指針的運算,比較多的一種說法是go團隊認爲指針的運算會帶來一些安全問題,再有就是簡化語法,因此go直接就不支持指針運算了。web

雖然go語法不支持,可是經過go的 unsafe 包能夠間接的實現對指針的運算,就想這個包的名字同樣,這個包提供的東西不是安全的,使用不當可能會出現一些問題,因此在go是不推薦使用的,本身玩玩還能夠,真正的項目中,仍是儘可能不要使用。安全

在一個知識點,go是經過首字母的大小寫在卻別 privatepublic 的,不一樣包中的是沒法訪問private變量的。其實所謂的private和public只是語法上的限制,到了彙編層,都是地址,哪來的公開和私有的,因此,可是咱們經過使用地址(也就是指針)來繞過語法限制,訪問其餘包中的私有變量。markdown

說了一堆廢話了,開始上真的,先建立有一個project,目錄層次以下:ui

上代碼

one.go

package one

type One struct {
	A int
	b int
	c *Two
}

複製代碼

two.go

package one

type Two struct {
	D int
}
複製代碼

main.go

package main

import (
	"fmt"
	"pointer/one"
	"unsafe"
)

func main() {
	o := &one.One{}

	p := unsafe.Pointer(o)
	p2 := (*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(o.A)))
	*p2 = 100

	t := &one.Two{D: 2000}
	fmt.Printf("%p \n", t)

	p3 := (*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(o.A)*2))

	*p3 = (int)(uintptr(unsafe.Pointer(t)))
	
	fmt.Println(o)

}
複製代碼

運行結果

能夠看到,one中私有變量已經被成功賦值了,url

經過打印的指針能夠看到, two 結構體的指針已經賦到 one 結構的私有變量 c 中了spa

代碼具體是什麼意思呢,簡單介紹一下3d

具體解釋

p := unsafe.Pointer(o) 把one結構的指針轉換成 unsafe.Pointer 類型。指針

這個類型有點像 C/C++ 中的 void*code

uintptr(p) 把指針轉換成能夠運算的類型

unsafe.Sizeof(o.A) 是獲取A變量在結構體中長度,對應的b就在結構體中的位置,也就是指針

由於 one中的b變量是 int 類型,因此要強轉成 int類型的指針,這時候就能夠給private變量b賦值了

一樣,也能夠爲c賦值,只不過c中是指針類型的,其實指針類型能夠看作是int類型,變量中保存的是內存地址罷了

(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(o.A)*2)) 一樣的方法拿到c的地址

(int)(uintptr(unsafe.Pointer(t))) 建立的two的指針地址轉換成 int 類型

好了,到如今整個指針的運算過程就結束了

換個形式

以前咱們是經過 unsafe.Sizeof(o.A) 獲取指針偏移長度的,其實咱們能夠根據變量的類型本身推算出來。

個人電腦是64位的,全部int 變量的長度也是64位,也就是8個字節,修改一下代碼

修改後

package main

import (
	"fmt"
	"pointer/one"
	"unsafe"
)

func main() {
	o := &one.One{}

	p := unsafe.Pointer(o)
	p2 := (*int)(unsafe.Pointer(uintptr(p) + 8))
	*p2 = 100

	t := &one.Two{D: 2000}
	fmt.Printf("%p \n", t)

	p3 := (*int)(unsafe.Pointer(uintptr(p) + 16))

	*p3 = (int)(uintptr(unsafe.Pointer(t)))

	fmt.Println(o)

}
複製代碼

運行結果

能夠發現結果是同樣的

可是第二種方式不夠通用,換個機器可能就報錯了,好比在32位的機器上,int長度是32位,程序就出錯了

總結

雖然 go 自己不支持指針的運算,可是有些時候咱們須要用到指針的運算,好比獲取private變量。可是這種方式有風險,在實際項目中使用要慎重!!!

相關文章
相關標籤/搜索