1.2. Pointer
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
包 reflect
下 Value
的 Pointer
方法和 UnsafeAddr
方法返回的是 uintptr
而不是 Pointer
类型,以便于调用者不使用 usafe
包就可以转换为任意类型。这也意味着,这两个方法的返回值必须使用 Pointer
进行转换才可以使用:
p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))
因为这两个函数调用的返回值是 uintptr
,所以也是不可以变量储存的。
1.2.6. reflect.SliceHeader
或者 reflect.StringHeader
的 Data
字段同 Pointer
的相互转换
前面说过,返回 uintptr
是为了调用者可以直接进行不同类型的转换,而不用导入 unsafe
包。这意味着,只有当指针解析为切片或者字符串时 SliceHeader
和 StringHeader
才可以被使用。
var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case)
hdr.Len = n
通常情况下, SliceHeader
和 StringHeader
只能作为 *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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论