警告一下柬唯!以下代碼均不是常規(guī)操作,且存在各種潛在不可控的風(fēng)險寨辩。在項目中應(yīng)用有可能被同事打死吓懈,慎用!C夷耻警!
1.調(diào)用其他包中公有結(jié)構(gòu)的私有成員變量
如果需要引用某個包中公有結(jié)構(gòu)體的私有變量,而這個變量又沒有提供對應(yīng)的訪問方法甸怕。那么如何繞過“小寫不公開”這個限制呢甘穿?簡單介紹一種方法直接通過變量地址訪問變量:
package other1
import "fmt"
type TestPointer struct {
A int
b int // 私有變量
}
func (T *TestPointer) OouPut() {
fmt.Println("TestPointer OouPut:", T.A, T.b)
}
package main
import (
"fmt"
"test/test4/other1"
"unsafe"
)
func main() {
T := other1.TestPointer{A:1}
pb := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&T)) + 8))
/*
Tmp := uintptr(unsafe.Pointer(&T)) + 8)
pb := (*int)(unsafe.Pointer(Tmp)
千萬不能出現(xiàn)這種用臨時變量中轉(zhuǎn)一下的情況。因為GC可能因為優(yōu)化內(nèi)存碎片的原因移動了這個對象梢杭。只保留了指針的地址是沒有意義的温兼。
*/
*pb = 2
T.OouPut() //1 2
}
用unsafe包中的unsafe.Pointer獲取到結(jié)構(gòu)體對象的首地址,然后加上想訪問的私有變量的偏移地址就是私有變量的地址武契。關(guān)于成員變量偏移量的問題請參閱 內(nèi)存對齊
2.調(diào)用其他包的私有func
go提供了一個編譯指令募判,繞過編譯器檢查荡含。直接訪問func的實現(xiàn)
//go:linkname
package other1
import "fmt"
func privateFunc() {
fmt.Println("this is private func")
}
package main
import (
_ "test/test4/other1"
_ "unsafe"
)
// call private func
//go:linkname private_func test/test4/other1.privateFunc
func private_func()
func main() {
private_func() // this is private func
}
關(guān)于編譯指令可以參閱 編譯指令 或 Command compile
上面代碼需要在調(diào)用者(這里是main.go)同目錄添加一個.s匯編文件,騙過編譯器届垫。讓編譯器認為是實現(xiàn)是在.s匯編文件中释液,從而跳過檢查
3. 調(diào)用其他包的公有結(jié)構(gòu)的私有方法
package other1
import "fmt"
type PublicStruct struct {
I int
b int
}
func (p *PublicStruct) f(a int) {
println("PublicStruct f()", p.I, p.b, a)
}
package main
import (
"test/test4/other1"
_ "unsafe"
)
// 調(diào)用其他包的公有結(jié)構(gòu)的私有func
//go:linkname public_struc_private_func test/test4/other1.(*PublicStruct).f
func public_struc_private_func(p *other1.PublicStruct, a int)
func main() {
// 先構(gòu)造一個other1.PublicStruct
p := &other1.PublicStruct{I:1}
public_struc_private_func(p, 100) // PublicStruct f() 1 0 100
}
和上面的類似用linkname指令騙過編譯器。這里聲明了一個指針接收者的func public_struc_private_func
第一個參數(shù)是對應(yīng)對象的指針装处,第二個參數(shù)開始是對應(yīng)func需要的參數(shù)误债。
其實這就是指針接收者func原本的實現(xiàn)方式(即 本質(zhì)上是一個普通的函數(shù),只是隱式傳遞了對象的指針)
4. 調(diào)用其他包的私有全局變量
package other1
var private_m = map[int]string {
1:"a",
}
import (
"fmt"
_ "test/test4/other1"
_ "unsafe"
) // 調(diào)用其他包的私有全局變量
//go:linkname private_member test/test4/other1.private_m
var private_member map[int]string
func main() {
fmt.Println(private_member[1]) // a
private_member[2] = "b"
for k, v := range private_member {
fmt.Println(k, v) // 1 a; 2 b
}
}
和上面的linkname類似妄迁,騙過編譯器寝蹈。直接訪問變量