返回介绍

1.2. Pointer

发布于 2024-10-12 12:11:02 字数 5073 浏览 0 评论 0 收藏 0

Pointer 定义:

type Pointer *ArbitraryType

Pointer 代表了一个指向任意类型的指针,有四种只适用对 Pointer 而不适用于其他类型的操作。

  • 任意类型的指针值可以被转换为一个 Pointer

  • 一个 Pointer 可以被转换为任意类型的指针值

  • 一个 uintptr 可以被转换为一个 Pointer

  • 一个 Pointer 也可以被转换为一个 uintptr

因此, Pointer 可以跳过类型系统而直接指向任意类型。所以需要十分小心的使用。

关于使用 Pointer 的规则,不使用这些规则的代码是不可用的,或者在未来是不可用的。

1.2.1. 使用 Pointer 作为中间者将 *T1 转换为 *T2

前提是 T2 的大小不超过 T1,而且两者的内存分布相同。

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

1.2.2. 把 Pointer 转换为 uintptr

Pointer 转换为 uintptr 将产生一个指向类型值的 int 变量。常用来打印一个 uintptr

uintptr 转换为 Pointer 是不可用的。

因为 uintptr 是一个整数值,而不是引用。就是说 uintptr 和指针没有任何关系。可以说是将 Pointer 指向的地址的值返回给 uintptr ,即使 uintptr 中的值对应的地址的对象更新了或者删除了, uintptr 也不会改变。

1.2.3. 把 Pointer 转为 uintptr 再转换回 Pointer ,其中带有 uintptr 数值运算

如果 Pointer 指向一个分配的对象,那么如下转换可以把 Pointer 指针向后移动。

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

最常用的是指向结构体中不同字段或者数组中的元素

// equivalent to f := unsafe.Pointer(&s.f)
 f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))
 // equivalent to e := unsafe.Pointer(&x[i])
 e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))

这可以用来向前或向后移动指针,通过加或者减 offset 。指针移动之后,也应该指向该内存范围中。

Pointer 移动超过其对象的原始内存分配范围是不可用的,如:

// 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))

当然如下代码也是错误的,因为 uintptr 不可以储存在变量中:

// INVALID: uintptr cannot be stored in variable
// before conversion back to Pointer.
u := uintptr(p)
p = unsafe.Pointer(u + offset)
Pointer`必须指向一个已经分配好的对象,而不能是`nil
// INVALID: conversion of nil pointer
u := unsafe.Pointer(nil)
p := unsafe.Pointer(uintptr(u) + offset)

1.2.4. 当调用 syscall.Syscall 时,需要把 Poiner 转换为 uintptr

syscall 包下的 Syscall 函数把 uintptr 参数传递给操作系统,然后根据调用的相关信息,把相应的 uintptr 再转换为指针。

如果一个指针参数必须被转换为 uintptr 作为参数的话,这个转换只能在调用函数中的参数表达式完成,因为 uintptr 是不能储存在变量中的。

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

编译器处理函数调用中的指针时,该指针所指向的对象会被保留到函数调用结束,即使该对象在函数调用时并不使用。

如下是错误的代码,因为 uintptr 不能保存在变量中

// 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))

1.2.5. 将 reflect.Value.Pointer 或者 reflect.Value.UnsafeAddr 的结果从 uintptr 转换为 Pointer

reflectValuePointer 方法和 UnsafeAddr 方法返回的是 uintptr 而不是 Pointer 类型,以便于调用者不使用 usafe 包就可以转换为任意类型。这也意味着,这两个方法的返回值必须使用 Pointer 进行转换才可以使用:

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

因为这两个函数调用的返回值是 uintptr ,所以也是不可以变量储存的。

1.2.6. reflect.SliceHeader 或者 reflect.StringHeaderData 字段同 Pointer 的相互转换

前面说过,返回 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

通常情况下, SliceHeaderStringHeader 只能作为 *SliceHeader*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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文