美文网首页Go
golang使用unsafe包实现指针运算操作private变量

golang使用unsafe包实现指针运算操作private变量

作者: QxQx | 来源:发表于2021-03-13 17:43 被阅读0次

golang中是有指针概念的,但是go中的指针功能被限制了,不像C/C++中那样,可以对指针进行运算

比如在C/C++中 *p++ 这样是正确的。但是在go中,这样写是错误的。至于go为什么会屏蔽指针的运算,比较多的一种说法是go团队认为指针的运算会带来一些安全问题,再有就是简化语法,所以go直接就不支持指针运算了。

虽然go语法不支持,但是通过go的 unsafe 包可以间接的实现对指针的运算,就想这个包的名字一样,这个包提供的东西不是安全的,使用不当可能会出现一些问题,所以在go是不推荐使用的,自己玩玩还可以,真正的项目中,还是尽量不要使用。

在一个知识点,go是通过首字母的大小写在却别 privatepublic 的,不同包中的是无法访问private变量的。其实所谓的private和public只是语法上的限制,到了汇编层,都是地址,哪来的公开和私有的,所以,但是我们通过使用地址(也就是指针)来绕过语法限制,访问其他包中的私有变量。

说了一堆废话了,开始上真的,先创建有一个project,目录层次如下:

目录

上代码

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)

}

运行结果

image

可以看到,one中私有变量已经被成功赋值了,

通过打印的指针可以看到, two 结构体的指针已经赋到 one 结构的私有变量 c 中了

代码具体是什么意思呢,简单介绍一下

具体解释

p := unsafe.Pointer(o) 把one结构的指针转换成 unsafe.Pointer 类型。

这个类型有点像 C/C++ 中的 void*

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

相关文章

  • golang使用unsafe包实现指针运算操作private变量

    golang中是有指针概念的,但是go中的指针功能被限制了,不像C/C++中那样,可以对指针进行运算 比如在C/C...

  • go指针

    定义 &变量 取地址操作 引用运算符 *指针变量 取值操作 解引用运算符 指针变量必...

  • java原子变量操作类以及实现原理分析

    java的原子变量操作类都是通过Unsafe的CAS方法实现。先看如何使用Unsafe实现原子自增。(有关Unsa...

  • JAVA并发-Atomic包

    Atomic包核心 Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作。compa...

  • Golang unsafe包使用

    unsafe包提供了访问底层内存的方法。是用unsafe函数可以提高访问对象的速度。通常用于对大数组的遍历。 un...

  • golang中的unsafe包详解

    一、unsafe 作用 从golang的定义来看,unsafe 是类型安全的操作。顾名思义,它应该非常谨慎地使用;...

  • Java Unsafe CAS 小试

    Unsafe 在 Java 标准库和框架中被大量使用,本文主要介绍如何使用 sun.misc.Unsafe 包实现...

  • Golang学习 - unsafe 包

    指针类型: *类型:普通指针,用于传递对象地址,不能进行指针运算。 unsafe.Pointer:通用指针类型,用...

  • 指针

    指针:一个变量,其值为另一个变量地址; 指针的算术运算 指针的递增 指针与数组的关系 可以使用指针的算术运算符来访...

  • golang中unsafe包教程

    unsafe内容介绍 unsafe包只有两个类型,三个函数,但是功能很强大。 unsafe 库让 golang 可...

网友评论

    本文标题:golang使用unsafe包实现指针运算操作private变量

    本文链接:https://www.haomeiwen.com/subject/hoixcltx.html