C中void***类型3D数组经cgo返回Go后首元素地址不符及解析疑问
问题解答
1. 为何3D指针的起始位置不等于[0][0][0]元素的地址?
你在C中创建的不是连续内存的多维数组,而是多层指针嵌套的结构,两者内存布局完全不同:
features是double***类型,它指向的是一块专门用来存放double**指针的数组(行指针数组),这块内存的地址就是你Go中打印的*result(0x3328990)。- 每个
features[i]又是指向另一块存放double*指针的数组(列指针数组),而features[i][j]才指向最终存放double元素的内存块。 features[0][0][0]的地址是features[0][0]指向的元素内存块的起始地址(0x33289c0),和行指针数组的内存是完全独立的两块区域,所以地址自然不相等。
你之前测试的**void和*void类型地址匹配,是因为那些是连续分配的数组(比如一维数组直接指向元素内存,或者二维数组是连续的单块内存),指针直接指向元素起始位置,而这里是多层指针嵌套的动态分配,每一层都有独立的指针数组内存。
2. Go中反向解析该3D数组的操作方法
需要借助Go的unsafe包逐层解析指针嵌套结构,同时要记得最后释放C侧分配的内存避免泄漏,示例代码如下:
import ( "fmt" "unsafe" ) func Data2featureWrapper4() { numRows := 3 numCols := 5 numDeps := 2 result := C.data13feature() // 第一层:将**unsafe.Pointer转为指向行指针数组的指针 rowPtrs := (*[1 << 30]unsafe.Pointer)(unsafe.Pointer(result))[:numRows:numRows] for i := 0; i < numRows; i++ { // 第二层:将每行的unsafe.Pointer转为指向列指针数组的指针 colPtrs := (*[1 << 30]unsafe.Pointer)(unsafe.Pointer(rowPtrs[i]))[:numCols:numCols] for j := 0; j < numCols; j++ { // 第三层:将每列的unsafe.Pointer转为指向double数组的指针 elements := (*[1 << 30]float64)(unsafe.Pointer(colPtrs[j]))[:numDeps:numDeps] // 打印元素值验证 fmt.Printf("features[%d][%d]: %v\n", i, j, elements) // 释放当前列的元素内存 C.free(unsafe.Pointer(colPtrs[j])) } // 释放当前行的列指针数组内存 C.free(unsafe.Pointer(rowPtrs[i])) } // 释放最外层的行指针数组内存 C.free(unsafe.Pointer(result)) }
代码说明:
- 使用
[1 << 30]unsafe.Pointer作为临时数组类型,是因为Go不允许直接创建长度不确定的数组,用一个足够大的长度(1<<30)来覆盖实际的数组长度,再通过切片截取到真实维度。 - 每一层解析后都要对应释放C侧
malloc的内存,顺序和C中分配的顺序相反(先释放元素,再列指针,最后行指针)。 float64对应C中的double类型,可以直接转换访问值。
内容的提问来源于stack exchange,提问作者yu xin
相关产品推荐
相关产品推荐

