当前位置: 首页 > 面试题库 >

如何在golang中从unsafe.Pointer指针创建数组或切片

公羊伟志
2023-03-14
问题内容

指向数组的指针,比方说:

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

我无法访问该变量array,上面的代码用于使其更加清晰。

另外,我知道数组的大小,但是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是整数,而不是引用。将Pointer转换为uintptr会创建一个没有指针语义的整数值。即使uintptr拥有某个对象的地址,垃圾回收器也不会在对象移动时更新该uintptr的值,该uintptr也不会使该对象被回收。

一般建议:unsafe尽可能远离包装。留在Go的类型安全中。

声明分片类型的变量,并使用不安全的转换来获取其
reflect.SliceHeader描述符。

然后,您可以修改其字段,将指针用作SliceHeader.Data值,将大小用作SliceHeader.LenSliceHeader.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并获取其长度之后,我们不再引用arrpuintptr当作参考),并且积极的GC可能会arr在我们准备打印之前data(正确地指向)删除。arr)。将a
runtime.KeepAlive()放在的末尾main()将确保arr在此调用之前不会被垃圾回收。有关详细信息,请参阅InGo中的变量何时不可达?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数据字段与指针进行转换。

与前面的情况一样,反射数据结构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



 类似资料:
  • 问题内容: 我正在使用FFmpeg为Windows平台编写一个应用程序,它是golang包装器goav,但是我在理解如何使用C指针获取对数组的访问方面遇到了麻烦。 我试图获取存储在AVFormatContext类中的流以供使用,并最终将帧添加到OpenGl中的纹理以使视频播放器具有出色的过渡效果。 我认为了解如何转换和访问C数据将使编码变得容易得多。 我已经删除了C代码的所有相关部分,包装程序和我

  • 在 C 语言中,数组名,&数组名,&数组首元素保存的都是同一个地址 #include <stdio.h> int main(){ int arr[3] = {1, 3, 5}; printf("%p\n", arr); // 0060FEA4 printf("%p\n", &arr); // 0060FEA4 printf("%p\n", &a

  • 值得注意点的是切片的本质就是一个指针指向数组,所以指向切片的指针是一个二级指针 package main import "fmt" func main() { // 1.定义一个切片 var sce[]int = []int{1, 3, 5} // 2.打印切片的地址 // 切片变量中保存的地址, 也就是指向的那个数组的地址 sce = 0xc0420620a0 f

  • 问题内容: 我有一个需要将对象数组存储在变量中的要求。对象是不同类型的。请参考以下示例: 注意第二个元素是字符串本身的数组。经过研究,我想将其存储为以下接口类型: 尽管如此,我还是遇到了一些无法找到的编译错误。 问题答案: 您要的是可能的- 游乐场链接: 但是您可能不想这样做。您正在与类型系统进行斗争,如果您这样做,我会质疑您为什么要使用Go。考虑利用类型系统- 游乐场链接:

  • 问题内容: 我正在使用FFmpeg为Windows平台编写一个应用程序,它是golang包装器goav,但是我在理解如何使用C指针获取对其所指向的数据数组的访问方面遇到了麻烦。 我正在尝试获取存储在AVFrame类中的数据,并使用Go将其写入文件,最后使用OpenGl中的纹理使视频播放器具有出色的过渡效果。 我认为了解如何转换和访问C数据将使编码变得容易得多。 我已经删除了C代码的所有相关部分,包

  • 问题内容: 我是Go的新手,正在尝试编写一个读取RPM文件的应用程序。每个块的开头都有的魔术字符。 这是我的结构 我正在尝试执行以下操作: 我在网上搜索,不确定如何从切片到数组(不进行复制)。我总是可以制作魔术(甚至是),但我对如何从类型转换为更好奇? 问题答案: 内置方法 copy 只会将一个切片复制到一个切片,而不是将切片复制到一个数组。 您必须欺骗 复制 以将数组视为切片 或使用for循环进