我試著將二維陣列從Go 傳遞到某個C 函數void foo(in **float, out *double)
。因為我想要這個 C 函數的包裝器,所以我希望 Go 函數有像 func FooWrapper([][]float32) []float64
這樣的定義。最簡單但效率不高的實作是透過下面列出的 C 來分配所有記憶體:
<code>func FooWrapper(values [][]float32) []float64 { totalObj := len(values) totalParams := len(values[0]) results := make([]float64, totalObj) ptrArrLength := uintptr(totalObj) * cPointerSize paramArrLength := uintptr(totalParams) * cFloatSize ptr := C.malloc(C.size_t(ptrArrLength + paramArrLength*uintptr(totalObj))) defer C.free(ptr) ptrSlice := unsafe.Slice((**C.float)(ptr), totalObj) for i, obj := range values { paramArrPtr := (*C.float)(unsafe.Add(ptr, ptrArrLength+uintptr(i)*paramArrLength)) ptrSlice[i] = paramArrPtr paramSlice := unsafe.Slice(paramArrPtr, totalParams) for j, param := range obj { paramSlice[j] = (C.float)(param) } } C.foo((**C.float)(ptr), (*C.double)(&results[0])) return results } </code>
這樣的實作安全嗎?我可以傳遞result
資料的指標嗎?據我所知,這個指標將被固定,因為它傳遞給了 C 函數。
但是我想分配更少的內存,只是重用Go內存,我已經了解了runtime.Pinner
,它使指針固定直到runtime.Pinner.Unpin()
調用。我嘗試使用 pinner 編寫另一個實作:
<code>func FooWrapper(values [][]float32) []float64 { length := len(values) results := make([]float64, length) pinner := runtime.Pinner{} defer pinner.Unpin() arr := (**C.float)(C.malloc(C.size_t(uintptr(length) * cPointerSize))) defer C.free(unsafe.Pointer(arr)) slice := unsafe.Slice(arr, length) for i, v := range values { pinner.Pin(&v[0]) slice[i] = (*C.float)(&v[0]) } C.foo(arr, (*C.double)(&results[0])) return results } </code>
但是,不幸的是,這段程式碼不起作用
runtime: pointer 0xc016ecbfc0 to unused region of span span.base()=0xc016eca000 span.limit=0xc016ecbfa0 span.state=1 fatal error: found bad pointer in Go heap (incorrect use of unsafe or cgo?)
我使用runtime.Pinner是否錯誤(據我所知,我可以固定切片資料)?或者這段程式碼還有另一個錯誤。除了將所有資料分配和複製到 C 記憶體之外,是否有一些將 3d(4d 等)數組傳遞給 C 函數的實現?
我找到了問題的答案。使用 malloc 是危險的,因為當我嘗試將 Go 指標寫入 C 記憶體時,GC 可以將未初始化的記憶體識別為「壞」內存,因此如果我將 malloc 更改為 calloc 就不會出現問題。當然這不是最好的解決方案,最好在 Go 中分配內存,因為它更快、更安全。
所有討論此處一个>.
#以上是使用runtime.Pinner將二維數組從Go傳遞到C的詳細內容。更多資訊請關注PHP中文網其他相關文章!