Go unsafe 包的使用

unsafe包

golang是一种静态的强类型的语言,所有的类型都是不能随意转换的,Go语言是不允许两个指针类型进行转换的。go官方是不推荐使用unsafe的操作因为它是不安全的,它绕过了golang的内存安全原则,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护。但是在很多地方却是很实用。在一些go底层的包中unsafe包被很频繁的使用。

unsafe 定义

package unsafe
//ArbitraryType仅用于文档目的,实际上并不是unsafe包的一部分,它表示任意Go表达式的类型。
type ArbitraryType int
//任意类型的指针,类似于C的*void
type Pointer *ArbitraryType
//确定结构在内存中占用的确切大小
func Sizeof(x ArbitraryType) uintptr
//返回结构体中某个field的偏移量
func Offsetof(x ArbitraryType) uintptr
//返回结构体中某个field的对其值(字节对齐的原因)
func Alignof(x ArbitraryType) uintptr

官方中定义了四个描述:

  1. 任何类型的指针都可以被转化为Pointer
  2. Pointer可以被转化为任何类型的指针
  3. uintptr可以被转化为Pointer
  4. Pointer可以被转化为uintptr

unsafe的使用

类型转换

使用unsafe可以实现类型的转换,下面的例子可以看到i是一个int类型,使用unsafe.Pointer转换成float64并且还修改了指针对应的值。

func main() {
    i := 10
    ip := &i

    fp := (*float64)(unsafe.Pointer(ip))

    *fp = *fp * 3

    fmt.Println(i)
}

// 结果: 30

但是使用起来要十分的小心,如果使用不当会引发错误。可以举一个例子:

func main() {
    i := 10
    ip := &i

    fp := (*string)(unsafe.Pointer(ip))

    *fp = "a"

    fmt.Println(i)
    // 结果:19678090
}

上面的误操作就是把int类型转成了string,并且修改了值导致结果出现了错误,并且这种错误

根据位移获取、修改对象的字段

利用unsafe的Pointer和Offsetof函数,可以获取对象的属性,并且可以修改对象的属性

type Student struct {
    Name string
    Age  int
}

func main() {
    s := Student{}
    s.Name = "Peter"
    s.Age = 33

    pStudent := unsafe.Pointer(&s)
    // 整个对象转换成指针,默认是获取第一个属性
    name := (*string)(unsafe.Pointer(pStudent))
    fmt.Println("name:", *name)
    // 利用Offsetof获取age属性的偏移量获取属性
    age := (*int)(unsafe.Pointer(uintptr(pStudent) + unsafe.Offsetof(s.Age)))
    fmt.Println("age:", *age)
    
    // 修改指针的值
    *name = "Mary"
    *age = 20
    fmt.Println(s)
}

获取私有变量

可以通过unsafe获取私有变量的值,也可以修改值。这个操作跟上面的获取值是一样的简单的例子如下:

type Teacher struct {
    name string
    age  int
}

func main() {
    t := Teacher{"ttt", 20}

    pt := unsafe.Pointer(&t)
    name := (*string)(unsafe.Pointer(pt))
    fmt.Println("name:", *name)
}

根据sizeof函数获取、修改

利用unsafe中的sizeof函数获取数组的值

func main() {
    array := []int{0, 1, -2, 3, 4}
    pointer := &array[0]
    fmt.Print(*pointer, " ")
    memoryAddress := uintptr(unsafe.Pointer(pointer)) + unsafe.Sizeof(array[0])
    for i := 0; i < len(array)-1; i++ {
        pointer = (*int)(unsafe.Pointer(memoryAddress))
        fmt.Print(*pointer, " ")
        memoryAddress = uintptr(unsafe.Pointer(pointer)) + unsafe.Sizeof(array[0])
    }
}
结果:0 1 -2 3 4 

大二小的宝
go 语言学习 经验总结记录
202 声望
73 粉丝
0 条评论
推荐阅读
服务器注册发现在Go微服务中的使用
服务注册:将提供某个服务的模块信息(通常是这个服务的IP和端口)注册到1个公共的租价你上去(比如:zookeeper、consul、etcd)

大二小的宝阅读 944

「刷起来」Go必看的进阶面试题详解
逃逸分析是Go语言中的一项重要优化技术,可以帮助程序减少内存分配和垃圾回收的开销,从而提高程序的性能。下面是一道涉及逃逸分析的面试题及其详解。

王中阳Go4阅读 1.9k评论 1

封面图
初学后端,如何做好表结构设计?
这篇文章介绍了设计数据库表结构应该考虑的4个方面,还有优雅设计的6个原则,举了一个例子分享了我的设计思路,为了提高性能我们也要从多方面考虑缓存问题。

王中阳Go4阅读 1.7k评论 2

封面图
滚蛋吧,正则表达式!
你是不是也有这样的操作,比如你需要使用「电子邮箱正则表达式」,首先想到的就是直接百度上搜索一个,然后采用 CV 大法神奇地接入到你的代码中?

良许4阅读 2.2k

又一款眼前一亮的Linux终端工具!
今天给大家介绍一款最近发现的功能十分强大,颜值非常高的一款终端工具。这个神器我是在其他公众号文章上看到的,但他们都没把它的强大之处介绍明白,所以我自己体验一波后,再向大家分享自己的体验。

良许5阅读 1.8k

一分钟搞明白!快速掌握 Go WebAssembly
最近因为各种奇怪的原因,更多的接触到了 WebAssembly。虽然之前很多博客也翻过写过各种文章,但总感觉欠些味道。于是今天梳理了一版,和大家一起展开学习。

煎鱼4阅读 2.1k

面试官:请说一下如何优化结构体的性能?
使用内存对齐机制优化结构体性能,妙啊!前言之前分享过2篇结构体文章:10秒改struct性能直接提升15%,产品姐姐都夸我好棒 和 Go语言空结构体这3种妙用,你知道吗? 得到了大家的好评。这篇继续分享进阶内容:结...

王中阳Go4阅读 3.7k评论 2

封面图
202 声望
73 粉丝
宣传栏