Golang运行时错误:CGO参数包含Go指针到Go指针
Golang运行时错误:CGO参数包含Go指针到Go指针 当从另一个Go代码调用以下函数时,我遇到了一个与C和Go指针相关的错误。错误发生的行已用注释"ERROR happens here"标出。我尝试了本论坛和其他地方建议的一些解决方案,但都没有成功。任何帮助都将不胜感激。我在linux/amd64上使用go1.11.12。
func LevmarExpr(e expr.Expr, searchDim int, task ExprProblemType, guess []float64, train, test []*PointSet) []float64 {
ps := train[0].NumPoints()
PS := len(train) * ps
c := make([]float64, len(guess))
var cd callback_data
cd.Train = train
cd.Test = test
cd.E = e
cd.Coeff = c
cd.Task = task
cd.J = make([]expr.Expr, len(guess))
for i, _ := range cd.J {
deriv := e.DerivConst(i)
// simp := deriv.Simplify(expr.DefaultRules())
cd.J[i] = deriv
}
// c/levmar inputs
coeff := make([]C.double, len(guess))
for i, g := range guess {
coeff[i] = C.double(g)
}
y := make([]C.double, PS)
for i1, T := range train {
for i2, p := range T.Points() {
i := i1*ps + i2
y[i] = C.double(p.Depnd(searchDim))
}
}
ya := (*C.double)(unsafe.Pointer(&y[0]))
ca := (*C.double)(unsafe.Pointer(&coeff[0]))
ni := C.int(PS)
mi := C.int(len(c))
// C.levmar_dif(ya, ca, mi, ni, unsafe.Pointer(&cd))
C.levmar_der(ya, ca, mi, ni, unsafe.Pointer(&cd)) // ERROR happens here
for i, _ := range coeff {
c[i] = float64(coeff[i])
}
return c
}
更多关于Golang运行时错误:CGO参数包含Go指针到Go指针的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang运行时错误:CGO参数包含Go指针到Go指针的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这个错误是由于在Go 1.11及更高版本中,cgo对Go指针传递到C代码的限制变得更加严格导致的。当C代码通过回调函数访问Go内存时,如果参数包含Go指针到Go指针的引用,就会触发这个运行时错误。
在你的代码中,cd结构体包含了Go指针(train、test、e等),当这个结构体被传递给C函数levmar_der时,就违反了cgo的指针传递规则。
解决方案是使用C内存来存储需要在C回调中访问的数据。以下是修复后的代码:
func LevmarExpr(e expr.Expr, searchDim int, task ExprProblemType, guess []float64, train, test []*PointSet) []float64 {
ps := train[0].NumPoints()
PS := len(train) * ps
c := make([]float64, len(guess))
// 为回调数据分配C内存
cd := (*C.callback_data_t)(C.malloc(C.size_t(unsafe.Sizeof(C.callback_data_t{}))))
defer C.free(unsafe.Pointer(cd))
// 初始化C内存中的回调数据
cd.train_count = C.int(len(train))
cd.test_count = C.int(len(test))
cd.ps = C.int(ps)
cd.search_dim = C.int(searchDim)
cd.task = C.int(task)
// 为训练数据分配C内存
cd.train_data = (*C.point_set_t)(C.malloc(C.size_t(unsafe.Sizeof(C.point_set_t{}) * uintptr(len(train)))))
defer C.free(unsafe.Pointer(cd.train_data))
// 为测试数据分配C内存
cd.test_data = (*C.point_set_t)(C.malloc(C.size_t(unsafe.Sizeof(C.point_set_t{}) * uintptr(len(test)))))
defer C.free(unsafe.Pointer(cd.test_data))
// 填充训练数据到C内存
for i, t := range train {
trainSet := (*C.point_set_t)(unsafe.Pointer(uintptr(unsafe.Pointer(cd.train_data)) + uintptr(i)*unsafe.Sizeof(C.point_set_t{})))
trainSet.point_count = C.int(t.NumPoints())
trainSet.points = (*C.point_t)(C.malloc(C.size_t(unsafe.Sizeof(C.point_t{}) * uintptr(trainSet.point_count))))
for j, p := range t.Points() {
point := (*C.point_t)(unsafe.Pointer(uintptr(unsafe.Pointer(trainSet.points)) + uintptr(j)*unsafe.Sizeof(C.point_t{})))
for k := 0; k < len(p.Indpnd()); k++ {
point.indpnd[k] = C.double(p.Indpnd()[k])
}
point.depnd = C.double(p.Depnd(searchDim))
}
}
// 类似的填充测试数据...
// 系数数组
coeff := make([]C.double, len(guess))
for i, g := range guess {
coeff[i] = C.double(g)
}
// 目标值数组
y := make([]C.double, PS)
for i1, T := range train {
for i2, p := range T.Points() {
i := i1*ps + i2
y[i] = C.double(p.Depnd(searchDim))
}
}
ya := (*C.double)(unsafe.Pointer(&y[0]))
ca := (*C.double)(unsafe.Pointer(&coeff[0]))
ni := C.int(PS)
mi := C.int(len(guess))
// 现在调用C函数不会触发指针错误
C.levmar_der(ya, ca, mi, ni, unsafe.Pointer(cd))
for i := range coeff {
c[i] = float64(coeff[i])
}
return c
}
你还需要在C代码中定义相应的结构体:
typedef struct {
double indpnd[10]; // 根据实际情况调整大小
double depnd;
} point_t;
typedef struct {
int point_count;
point_t* points;
} point_set_t;
typedef struct {
int train_count;
int test_count;
int ps;
int search_dim;
int task;
point_set_t* train_data;
point_set_t* test_data;
} callback_data_t;
关键点是:
- 所有需要在C回调中访问的数据都必须分配在C内存中
- 使用
C.malloc和C.free来管理C内存 - 避免在C函数参数中直接传递包含Go指针的结构体
- 使用
defer确保C内存被正确释放
这种方法确保了C代码只操作C内存,避免了Go指针传递的限制。

