[英]Go: how to convert unsafe.Pointer into pointer to array with unknown length?
[英]How to create an array or a slice from an array unsafe.Pointer in golang?
一个指向数组的指针,让我们说:
p := uintptr(unsafe.Pointer(&array))
size := 5
我无法访问变量array
,上面的代码用于使其更清晰。
另外,我知道数组的size
,但是size
不是常量,它会根据运行时间而变化。
现在,我想用已知的指针,大小以及数据类型初始化切片或数组。
我想出了以下代码:
data := make([]byte, size)
stepSize := unsafe.Sizeof(data[0])
for i := 0; i < size; i++ {
data[i] = *(*byte)(unsafe.Pointer(p))
p += stepSize
}
fmt.println(data)
但是这个方法会进行内存复制,这可能是效率低下的,无论如何都没有复制?
PS我也试过以下两种方法,
// method 1
data := *(*[]byte)(unsafe.Pointer(p))
// method 2
data := *(*[size]byte)(unsafe.Pointer(p))
但它会在运行时失败,我现在知道它的原因。
前言:
您应该知道:如果您将指针作为uintptr
类型的值,则不会阻止原始数组进行垃圾回收( uintptr
值不计入引用)。 所以在使用这样的值时要小心,不能保证它会指向有效的值/内存区域。
从包unsafe.Pointer
引用:
uintptr是整数,而不是引用。 将指针转换为uintptr会创建一个没有指针语义的整数值。 即使uintptr保存某个对象的地址,如果对象移动,垃圾收集器也不会更新该uintptr的值,uintptr也不会保留该对象的回收。
一般建议:尽可能远离包装unsafe
。 留在Go的类型安全。
声明切片类型的变量,并使用不安全的转换来获取其reflect.SliceHeader
描述符。
然后,您可以使用指针作为SliceHeader.Data
值修改其字段,并将大小修改为SliceHeader.Len
和SliceHeader.Cap
。
完成此操作后,slice变量将指向与初始指针相同的数组。
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可能在我们到达点之前正确地擦除arr
打印data
(指向arr
)。 将runtime.KeepAlive()
放在main()
的末尾将确保在此调用之前不会对arr
进行垃圾回收。 有关详细信息,请参阅In Go,变量何时无法访问? 如果指针的供应商确保它不会被垃圾收集,则不需要在代码中调用runtime.KeepAlive()
。
或者,您可以使用复合文字创建一个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数据字段转换为Pointer或从指针转换。
与前一种情况一样,反射数据结构SliceHeader和StringHeader将字段Data声明为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
在这种用法中,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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.