簡體   English   中英

Go:如何將 unsafe.Pointer 轉換為指向未知長度數組的指針?

[英]Go: how to convert unsafe.Pointer into pointer to array with unknown length?

我正在嘗試編寫一個 Go 程序,它使用mmap到 map 一個包含float32值的非常大的文件到 memory。這是我的嘗試(受先前答案的啟發,為簡潔起見省略了錯誤處理):

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])
    }
}

這失敗並顯示以下錯誤消息:

./main.go:21: non-constant array bound n

由於n由文件的長度決定(在編譯時未知),我不能在轉換中用常量值替換n 如何將mmap轉換為float32值的數組(或切片)?

您首先將其轉換為靜態長度可以適合您數據的類型的數組,然后將該數組切成正確的長度和容量。

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

不幸的是,在您的情況下,您無法獲得指向數組的指針。 這是因為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 的回答中給出——將mmapData轉換為unsafe.Pointer ,然后將其轉換為任意大的指向數組的指針,最后將該數組切片指定為所需的大小和容量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM