前言:
您应该知道:如果您将指针作为 uintptr 类型的值,这不会阻止原始数组被垃圾收集(uintptr 值不计为引用)。所以在使用这样的值时要小心,不能保证它会指向一个有效的值/内存区域。
引用包unsafe.Pointer:
uintptr 是一个整数,而不是一个引用。将指针转换为 uintptr 会创建一个没有指针语义的整数值。即使一个 uintptr 持有某个对象的地址,如果该对象移动,垃圾收集器也不会更新该 uintptr 的值,该 uintptr 也不会阻止该对象被回收。
一般建议:尽可能远离包裹unsafe。留在 Go 的类型安全范围内。
声明一个切片类型的变量,并使用不安全的转换来获取它的reflect.SliceHeader描述符。
然后你可以修改它的字段,使用指针作为SliceHeader.Data的值,大小作为SliceHeader.Len和SliceHeader.Cap。
完成此操作后,切片变量将指向与初始指针相同的数组。
arr := [10]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
size := len(arr)
p := uintptr(unsafe.Pointer(&arr))
var data []byte
sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
sh.Data = p
sh.Len = size
sh.Cap = size
fmt.Println(data)
runtime.KeepAlive(arr)
输出(在Go Playground上试试):
[0 1 2 3 4 5 6 7 8 9]
请注意,我使用了runtime.KeepAlive()。这是因为在获取arr 的地址并得到它的长度之后,我们不再引用arr(p 被uintptr 算作引用),并且积极的GC 可能——正确地——在我们开始打印data(指向arr)之前擦除arr。将runtime.KeepAlive() 放在main() 的末尾将确保arr 不会被垃圾回收
在这个电话之前。有关详细信息,请参阅In Go, when will a variable become unreachable? 如果指针的提供者确保它不会被垃圾回收,则无需在代码中调用runtime.KeepAlive()。
或者,您可以使用composite literal 创建reflect.SliceHeader,并使用不安全的转换从中获取切片,如下所示:
sh := &reflect.SliceHeader{
Data: p,
Len: size,
Cap: size,
}
data := *(*[]byte)(unsafe.Pointer(sh))
fmt.Println(data)
runtime.KeepAlive(arr)
输出将是相同的。在Go Playground 上试试这个。
这种可能性/用例记录在unsafe.Pointer,带有警告和警告:
(6) reflect.SliceHeader 或 reflect.StringHeader 数据字段与指针的转换。
与前一种情况一样,反射数据结构 SliceHeader 和 StringHeader 将字段 Data 声明为 uintptr 以防止调用者在未先导入“不安全”的情况下将结果更改为任意类型。但是,这意味着 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
在这种用法中,hdr.Data 实际上是引用切片头中的底层指针的另一种方式,而不是 uintptr 变量本身。
一般来说,reflect.SliceHeader 和 reflect.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