问题描述

一个指向数组的指针,比如说:

A pointer to an array, let's say:

p := uintptr(unsafe.Pointer(&array))
size := 5
array
array
size
size

现在,我想使用已知的指针,大小和数据类型初始化切片或数组.

Now, I want to initialize a slice or an array with the known pointer, size and of course the data type.

我想出了以下代码:

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)

但是这种方法会进行内存复制吗,可能效率很低,是否不进行复制就存在吗?

but this method does memory copy, which might be inefficient, is there anyway without doing the copy?

P.S.我还尝试了以下两种方法,

P.S. I also tried the following two methods,

// method 1
data := *(*[]byte)(unsafe.Pointer(p))
// method 2
data := *(*[size]byte)(unsafe.Pointer(p))

但是它将在运行时失败,我现在知道其原因.

but it will fail at runtime and I know its reason now.

推荐答案

前言:

uintptruintptr
uintptruintptr
unsafe.Pointer
unsafe.Pointer

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

A uintptr is an integer, not a reference. Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.

unsafe
unsafe
reflect.SliceHeader
reflect.SliceHeader
SliceHeader.DataSliceHeader.LenSliceHeader.Cap
SliceHeader.DataSliceHeader.LenSliceHeader.Cap

完成此操作后,slice变量将指向与初始指针相同的数组.

Once you're done with this, the slice variable will point to the same array as your initial pointer.

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)

输出(在转到操场上尝试):

[0 1 2 3 4 5 6 7 8 9]
runtime.KeepAlive()arrarrpuintptrdataarrarrruntime.KeepAlive()main()arrruntime.KeepAlive()
runtime.KeepAlive()arrarrpuintptrarrdataarrruntime.KeepAlive()main()arrruntime.KeepAlive()
reflect.SliceHeader
reflect.SliceHeader
sh := &reflect.SliceHeader{
    Data: p,
    Len:  size,
    Cap:  size,
}

data := *(*[]byte)(unsafe.Pointer(sh))

fmt.Println(data)

runtime.KeepAlive(arr)

输出将相同.在去游乐场上尝试一下.

Output will be the same. Try this one on the Go Playground.

unsafe.Pointer

(6)将reflect.SliceHeader或reflect.StringHeader数据字段与指针进行转换.

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

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

As in the previous case, the reflect data structures SliceHeader and StringHeader declare the field Data as a uintptr to keep callers from changing the result to an arbitrary type without first importing "unsafe". However, this means that SliceHeader and StringHeader are only valid when interpreting the content of an actual slice or string value.

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变量本身.

In this usage hdr.Data is really an alternate way to refer to the underlying pointer in the slice header, not a uintptr variable itself.

通常,reflect.SliceHeader和reflect.StringHeader只能用作指向实际切片或字符串的* reflect.SliceHeader和* reflect.StringHeader,而不能用作纯结构.程序不应声明或分配这些结构类型的变量.

In general, reflect.SliceHeader and reflect.StringHeader should be used only as *reflect.SliceHeader and *reflect.StringHeader pointing at actual slices or strings, never as plain structs. A program should not declare or allocate variables of these struct types.

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

这篇关于如何使用golang中的unsafe.Pointer指针从数组创建数组或切片?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!