为了账号安全,请及时绑定邮箱和手机立即绑定

Go:如何将 unsafe.Pointer 转换为指向长度未知的数组的指针?

Go:如何将 unsafe.Pointer 转换为指向长度未知的数组的指针?

阿晨1998 2022-03-07 15:53:37
我正在尝试编写一个 Go 程序,该程序用于将mmap包含float32值的非常大的文件映射到内存中。这是我的尝试(受先前答案的启发,为简洁起见省略了错误处理):package mainimport (    "fmt"    "os"    "syscall"    "unsafe")func main() {    fileName := "test.dat"    info, _ := os.Stat(fileName)    fileSize := info.Size()    n := int(fileSize / 4)    mapFile, _ := os.Open(fileName)    defer mapFile.Close()    mmap, _ := syscall.Mmap(int(mapFile.Fd()), 0, int(fileSize),        syscall.PROT_READ, syscall.MAP_SHARED)    defer syscall.Munmap(mmap)    mapArray := (*[n]float32)(unsafe.Pointer(&mmap[0]))    for i := 0; i < n; i++ {        fmt.Println(mapArray[i])    }}这失败并显示以下错误消息:./main.go:21: non-constant array bound n由于n由文件的长度决定(在编译时未知),因此我无法用强制转换n中的常量值替换。如何转换mmap为float32值的数组(或切片)?
查看完整描述

2 回答

?
慕斯王

TA贡献1864条经验 获得超2个赞

您首先转换为具有可以适合您的数据的静态长度的类型的数组,然后将该数组切片为正确的长度和容量。

mapSlice := (*[1 << 30]float32)(unsafe.Pointer(&mmap[0]))[:n:n]


查看完整回答
反对 回复 2022-03-07
?
繁星coding

TA贡献1797条经验 获得超4个赞

不幸的是,在您的情况下,您无法获得指向数组的指针。这是因为n它不是一个常数值(即它是在运行时用 确定的fileSize/4)。(注意如果fileSize是常数,你可以得到一个数组。)


虽然有安全和不安全的选择。


安全的,或者有些人可能称之为“正确”的方式——这需要一个副本,但你可以控制字节序。这是一个例子:


import (

    "encoding/binary"

    "bytes"

    "unsafe" // optional

)


const SIZE_FLOAT32 = unsafe.Sizeof(float32(0)) // or 4


bufRdr := bytes.NewReader(mmap)

mapSlice := make([]float32, len(mmap)/SIZE_FLOAT32) // = fileSize/4

err := binary.Read(bufRdr, binary.LittleEndian, mapSlice) // could pass &mapSlice instead of mapSlice: same result.

// mapSlice now can be used like the mapArray you wanted.

有几种方法可以不安全地做到这一点,但在 Go 1.17 中它非常简单。


mapSlice := unsafe.Slice((*float32)(unsafe.Pointer(&mmap[0])), len(mmap)/SIZE_FLOAT32)

你也可以使用reflect.SliceHeader. 为了防止垃圾收集器问题,这里有很多细微差别需要注意:


var mapSlice []float32 // mapSlice := []float32{} also works (important thing is that len and cap are 0)


// newSh and oldSh are here for readability (i.e. inlining these variables is ok, but makes things less readable IMO)

newSh := (*reflect.SliceHeader)(unsafe.Pointer(&mapSlice))

oldSh := (*reflect.SliceHeader)(unsafe.Pointer(&mmap))


// Note: order of assigning Data, Cap, Len is important (due to GC)

newSh.Data = oldSh.Data

newSh.Cap = oldSh.Cap/SIZE_FLOAT32

newSh.Len = oldSh.Len/SIZE_FLOAT32


runtime.KeepAlive(mmap) // ensure `mmap` is not freed up until this point.

unsafe@JimB 的回答中给出了我能想到的最后一种方法——将 anmmap转换为Dataan unsafe.Pointer,然后将其转换为一个任意大的指向数组的指针,最后将该数组切片指定为所需的大小和容量。


查看完整回答
反对 回复 2022-03-07
  • 2 回答
  • 0 关注
  • 223 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号