如何将C.double数组传递给Cgo函数?

时间:2018-11-30 00:16:15

标签: go cgo

我刚刚开始使用CGo,并且试图将数据发送到C库,该库对浮点数/双精度数组进行统计计算。我现在想弄清楚的是如何将浮点数或C.double的数组发送到具有如下签名的CGo函数:

double pop_mean(int numPoints, double a[])

我已经找到了如何将C.int放入其中,但是在弄清楚如何发送双打数组时遇到了麻烦。

我还没有看过任何有关此确切内容的博客文章或SO问题,所以我想问一下。

以下是我到目前为止的最大努力。

// Get a basic function to work, while passing in an ARRAY  arr := make([]C.double, 0)
arr = append(arr, C.double(10.0))
arr = append(arr, C.double(20.0))
arr = append(arr, C.double(30.0))
var fixedArray [3]C.double = arr[:]

// ptr := C.CBytes(arr)
// defer C.free(unsafe.Pointer(ptr))

coolMean := C.pop_mean(3, &fixedArray)
fmt.Println("pop_mean (10, 20, 30): ", coolMean)

这是我得到的错误:

./main.go:64:6: cannot use arr[:] (type []_Ctype_double) as type [3]_Ctype_double in assignment
./main.go:69:35: cannot use &fixedArray (type *[3]_Ctype_double) as type *_Ctype_double in argument to _Cfunc_pop_mean

如何将C.double数组传递给代码?

2 个答案:

答案 0 :(得分:3)

  

将数组名称传递给函数时,传递的是   初始元素的位置。在调用的函数中,此   参数是局部变量,因此数组名称参数是   指针,即包含地址的变量。

     

C Programming Language, 2nd Edition


  

Slice types

     

切片是基础层连续段的描述符   数组,并提供对该数组中元素编号序列的访问   数组。

     

像数组一样,切片是可索引的并且具有长度。一个的长度   可以通过内置函数len查找slice;不像   在执行期间可能会更改的数组。元素可以解决   整数索引0到len(s)-1。给定的切片索引   元素可能小于相同元素在索引中的索引   基础数组。

     

切片一旦初始化,便始终与基础   包含其元素的数组。

     

The Go Programming Language Specification


  

参考:Go Command cgo


对于切片apop_mean(int numPoints, double a[]) C函数的参数为​​len(a)(切片基础数组的长度)和&a[0](第一个的地址)切片基础数组的元素。


在Go中,我们经常在函数中隐藏细节。例如,一个popMean函数,

package main

import (
    "fmt"
)

/*
double pop_mean(int numPoints, double a[]) {
    if (a == NULL || numPoints == 0) {
        return 0;
    }
    double mean = 0;
    for (int i = 0; i < numPoints; i++) {
        mean+=a[i];
    }
    return mean / numPoints;
}
*/
import "C"

func popMean(a []float64) float64 {
    // This is the general case, which includes the special cases
    // of zero-value (a == nil and len(a) == 0)
    // and zero-length (len(a) == 0) slices.
    if len(a) == 0 {
        return 0
    }
    return float64(C.pop_mean(C.int(len(a)), (*C.double)(&a[0])))
}

func main() {
    a := make([]float64, 10)
    for i := range a {
        a[i] = float64(i + 1)
    }

    // slice
    fmt.Println(len(a), a)
    pm := popMean(a)
    fmt.Println(pm)

    // subslice
    b := a[1:4]
    fmt.Println(len(b), b)
    pm = popMean(b)
    fmt.Println(pm)

    // zero length
    c := a[:0]
    fmt.Println(len(c), c)
    pm = popMean(c)
    fmt.Println(pm)

    // zero value (nil)
    var z []float64
    fmt.Println(len(z), z, z == nil)
    pm = popMean(z)
    fmt.Println(pm)
}

输出:

10 [1 2 3 4 5 6 7 8 9 10]
5.5
3 [2 3 4]
3
0 []
0
0 [] true
0

答案 1 :(得分:-1)

我发现您必须向数组中的第一个值发送指针,而不是向切片的第一个元素或切片本身发送指针。

并且我还遇到了一个问题,我创建了一个新变量,该变量被分配给切片中第一项的值,后来又创建了一个指向该变量的指针(该指针不再是原始数组的一部分),而不是创建指向数组第一项的指针(就像我想要的那样)。

下面是工作代码,并带有注释,以帮助避免上一段中的问题。

// Get a basic function to work, while passing in an ARRAY

// Create a dummy array of (10,20,30), the mean of which is 20.
arr := make([]C.double, 0)
arr = append(arr, C.double(10.0))
arr = append(arr, C.double(20.0))
arr = append(arr, C.double(30.0))
firstValue := &(arr[0]) // this notation seems to be pretty important... Re-use this!
// if you don't make it a pointer right away, then you make a whole new object in a different location, so the contiguous-ness of the array is jeopardized.
// Because we have IMMEDIATELY made a pointer to the original value,the first value in the array, we have preserved the contiguous-ness of the array.
fmt.Println("array length: ", len(arr))

var arrayLength C.int
arrayLength = C.int(len(arr))
// arrayLength = C.int(2)

fmt.Println("array length we are using: ", arrayLength)

arrayMean := C.pop_mean(arrayLength, firstValue)
fmt.Println("pop_mean (10, 20, 30): ", arrayMean)

这将产生以下结果:

array length:  3
array length we are using:  3
pop_mean (10, 20, 30):  20

或者,如果我们取消注释将arrayLength更改为2的行,则会得到以下结果:

array length:  3
array length we are using:  2
pop_mean (10, 20, 30):  15