首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Go:如何将unsafe.Pointer转换为长度未知的数组指针?

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

Stack Overflow用户
提问于 2016-08-29 21:19:19
回答 2查看 747关注 0票数 3

我正在编写一个Go程序,它使用mmap将包含float32值的非常大的文件映射到内存中。下面是我的尝试(受previous answer的启发,为了简洁起见省略了错误处理):

代码语言:javascript
复制
package main

import (
    "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])
    }
}

这与以下错误消息失败:

代码语言:javascript
复制
./main.go:21: non-constant array bound n

因为n是由文件的长度决定的(编译时不知道),所以我不能用强制转换中的常量值替换n。如何将mmap转换为float32值的数组(或切片)?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-08-29 21:24:02

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

代码语言:javascript
复制
mapSlice := (*[1 << 30]float32)(unsafe.Pointer(&mmap[0]))[:n:n]
票数 3
EN

Stack Overflow用户

发布于 2021-08-19 19:03:26

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

不过,也有安全和不安全的替代办法。

保险柜,或一些人可能称之为“正确”的方式-这需要一个副本,但你可以控制的endianness。下面是一个例子:

代码语言:javascript
复制
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非常简单。

代码语言:javascript
复制
mapSlice := unsafe.Slice((*float32)(unsafe.Pointer(&mmap[0])), len(mmap)/SIZE_FLOAT32)

您也可以使用reflect.SliceHeader。为了防止垃圾收集器问题,这里需要注意许多细微之处:

代码语言:javascript
复制
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的答案中给出的--将mmapData转换为unsafe.Pointer,然后将其转换为任意大的数组指针,然后将指定的数组分割到所需的大小和容量。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39215392

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档