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变量。可是这种方式有风险,在实际项目中使用要慎重!!!

相关文章
相关标签/搜索