专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Golang unsafe包使用模式

Pointer代表一个指向任意类型的指针。有四种特殊的操作,可以在Pointer类型上进行,但对其他类型并不能使用:

  • 任意类型的指针都可以转化为一个Pointer
  • 一个Pointer可以转化为任意类型的指针
  • 一个uintptr可以转化为一个Pointer
  • 一个Pointer可以转化为一个uintptr

因此,指针允许程序越过类型系统并读写任意内存。 使用时应格外小心。

以下涉及Pointer的模式是有效的

不使用这些模式的代码今天可能无效,或者将来变得无效。即使以下有效模式也带有重要的警告。

1.Conversion of a *T1 to Pointer to *T2.

T2不大于T1,并且两个共享相同的内存布局,这种转换允许将一种类型的数据重新解释为另一种类型的数据.例如math.Float64bits的实现:

func Float64bits(f float64) uint64 {
        return *(*uint64)(unsafe.Pointer(&f))
}

2. Conversion of a Pointer to a uintptr (but not back to Pointer).

Pointer转换为uintptr会生成所指向的值的内存地址(整数)。 这种uintptr的通常用法是打印它。

将uintptr转换回Pointer通常是无效的。

uintptr是整数,不是引用。Pointer转换为uintptr会创建一个没有指针语义的整数值。 即使uintptr保留了某个对象的地址,垃圾回收器也不会在对象移动时更新该uintptr的值,该uintptr也不会使该对象被回收

其余模式枚举了从uintptrPointer的唯一有效转换。

3.Conversion of a Pointer to a uintptr and back, with arithmetic.

如果p指向已分配的对象,则可以通过转换为uintptr,添加偏移量并将其转换回Pointer的方式进入对象。

p = unsafe.Pointer(uintptr(p) + offset)

此模式最常见的用法是访问结构或数组元素中的字段

// 二者是等效的 f := unsafe.Pointer(&s.f)
//  f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))

// 二者是等效的 e := unsafe.Pointer(&x[i])
//  e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))

以这种方式从指针添加和减去偏移量都是有效的。在所有情况下,结果都必须继续指向原始分配的对象。

与C语言不同,将指针移到其原始分配的末尾是无效的:

    // INVALID: end points outside allocated space.
    var s thing
    end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s))

    // INVALID: end points outside allocated space.
    b := make([]byte, n)
    end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n))

请注意,两个转换必须出现在相同的表达式中,并且它们之间只有中间的算术:

    // INVALID: uintptr cannot be stored in variable
    // before conversion back to Pointer.
    u := uintptr(p)
    p = unsafe.Pointer(u + offset)

请注意,指针必须指向已分配的对象 ,因此不可能会为nil

    // INVALID: conversion of nil pointer
    u := unsafe.Pointer(nil)
    p := unsafe.Pointer(uintptr(u) + offset)

4.Conversion of a Pointer to a uintptr when calling syscall.Syscall.

软件包syscall中的Syscall函数将其uintptr参数直接传递给操作系统,然后,操作系统可以根据调用的详细信息将其中一些参数重新解释为指针。 也就是说,系统调用实现正在将某些参数从uintptr隐式转换回指针。

如果必须将指针参数转换为uintptr以用作参数,则该转换必须出现在调用表达式本身中:

syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))

编译器处理汇编函数调用的参数列表中Pointeruintptr转换,方法是安排保留引用的分配对象(如果有),直到调用完成后才移动,即使从类型本身来看,调用期间对象不再需要。

为了使编译器能够识别这种模式,转换必须出现在参数列表中:

    // INVALID: uintptr cannot be stored in variable
    // before implicit conversion back to Pointer during system call.
    u := uintptr(unsafe.Pointer(p))
    syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))

5.Conversion of the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr from uintptr to Pointer.

reflect的名为PointerUnsafeAddrValue方法返回uintptr而不是unsafe.Pointer类型,以防止调用者将结果更改为任意类型,而无需首先导入unsafe。 但是,这意味着结果很脆弱,必须在调用后立即使用相同的表达式将其转换为Pointer

p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))

与上述情况一样,在转换之前存储结果无效:

    // INVALID: uintptr cannot be stored in variable
    // before conversion back to Pointer.
    u := reflect.ValueOf(new(int)).Pointer()
    p := (*int)(unsafe.Pointer(u))

6.Conversion of a reflect.SliceHeader or reflect.StringHeader Data field to or from Pointer.

与前面的情况一样,反射数据结构SliceHeaderStringHeader将字段Data声明为uintptr,以防止调用者在不首先导入unsafe的情况下将结果更改为任意类型。 但是,这意味着SliceHeaderStringHeader仅在解释实际切片或字符串值的内容时才有效。

    var s string
    hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
    hdr.Data = uintptr(unsafe.Pointer(p))              // case 6 (this case)
    hdr.Len = n

在这种用法中,hdr.Data实际上是引用字符串头部的指针的替代方法,而不是uintptr变量本身 。

通常,reflect.SliceHeaderreflect.StringHeader只能用作指向实际切片或字符串的* reflect.SliceHeader* reflect.StringHeader,而不能用作纯结构。

程序不应声明或分配这些结构类型的变量。

    // INVALID: a directly-declared header will not hold Data as a reference.
    var hdr reflect.StringHeader
    hdr.Data = uintptr(unsafe.Pointer(p))
    hdr.Len = n
    s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost

文章永久链接:https://tech.souyunku.com/41010

未经允许不得转载:搜云库技术团队 » Golang unsafe包使用模式

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们