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指针(trainteste等),当这个结构体被传递给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;

关键点是:

  1. 所有需要在C回调中访问的数据都必须分配在C内存中
  2. 使用C.mallocC.free来管理C内存
  3. 避免在C函数参数中直接传递包含Go指针的结构体
  4. 使用defer确保C内存被正确释放

这种方法确保了C代码只操作C内存,避免了Go指针传递的限制。

回到顶部