This is a creation in Article, where the information may have evolved or changed.
Recently on the Kubernetes to integrate other team algorithm things, their algorithm is written in C, naturally I need to use CGO to invoke. This article has organized some functions in the process of using CGO, which are hereby collated and recorded.
1, passing a two-dimensional integer array to the C function
The C function needs to receive a two-dimensional integer array as a parameter,
extern bool xxx_func(int** _matrix);
The initial implementation is as follows:
goArray := [][]int{{1},{3,3},{3,3,2}}cArray := make([][]C.int, len(goArray))for i, _ := range goArray { cArray[i] = make([]C.int, len(goArray[i])) for j, _ := range goArray[i] { cArray[i][j] = C.int(goArray[i][j]) }} C.xxx_func((**C.int)(unsafe.Pointer(&cArray[0][0]))) // 把该数组的首地址传递给c函数
Is that the way it's done? Of course not! Looking closely at the function, this function actually creates a slice, then stores n separate slices in it, and its memory address is not contiguous! Of course, this is true for go itself, but for C it is a big mistake, the memory address of the two-dimensional array in C must be contiguous (the two-dimensional array in C is actually a one-dimensional array, addressing differently). So it should be done as follows:
- Assigns a slice of length n*m (N and m for rows and columns of a two-dimensional array)
- Populate this slice with values from the two-dimensional array of Go
- Pass this slice pointer to the C function
The first type of code is implemented as follows:
n, m := len(goArray), 0for _, row := range goArray { if len(row) > m { m = len(row) }}cArray := make([]C.int, n*m)for i, row := range goArray { for j, v := range row { a[i * n + j] = v }
Then run, found cannot use &cArray[0] (type *C.int) as type **C.int in argument to func literal
that the above operation is actually to convert the two-dimensional array of go to a one-dimensional array and then passed to the C function, but the C function to receive is a int**
two-dimensional array, so error, the best solution is to change the C function parameters to receive int*
a one-dimensional array.
But what if we can't change the parameters of the C function?
The second type of code implementation:
cArray := make([]*C.int, len(goArray)) for i, row := range goArray { p := (*C.int)(C.malloc(C.size_t(C.sizeof_int * len(row)))) cArray[i] = p pa := (*[1 << 30]C.int)(unsafe.Pointer(p)) for j, v := range row { (*pa)[j] = C.int(v) } }
Here, we assign a source-sized c.int array for each row of the Goarray, and give the pointer p to CArray,
The P type *C.int
is then converted to *[1<<30]C.int
a type (pointing to a large array of c.int types), but there are two questions to note:
- Memory release for C.malloc ()
- We
row
have allocated an array for each row, but the array length information is missing! There's no problem with go, but for C, an array is just a pointer, and it doesn't know the length of the memory area through this pointer, you have to tell it, or write a struct that contains the pointer and length, which depends on the specific implementation of C.
The first method is recommended, because the memory release problem of CGO and C can toss you for a few days, unless you know clearly where to release and where not to release.
2, passing a string array to the C function
This is much simpler, but you need to be aware of the memory release. The code is as follows:
goString := []string{"w0", "w1", "w2", "w3"} cString := make([]*C.char, len(goString)) for i, _ := range goString { cString[i] = C.CString(goString[i]) // 注意这个内存释放,该不该在调用完c函数之后释放,依赖于c那边的实现,这里要小心使用! defer C.free(unsafe.Pointer(cString[i])) } C.xxx_func2((**C.char)(unsafe.Pointer(&cString[0])))
Official documentation on C.cstring:
// Go string to C string// The C string is allocated in the C heap using malloc.// It is the caller's responsibility to arrange for it to be// freed, such as by calling C.free (be sure to include stdlib.h// if C.free is needed).func C.CString(string) *C.char
3, convert c string array to go string slice
//你必须清楚地知道返回的c字符串数组的长度func GoStrings(length int, argv **C.char) []string { if argv == nil { return nil } tmpslice := (*[1 << 30]*C.char)(unsafe.Pointer(argv))[:length:length] gostrings := make([]string, length) for i, s := range tmpslice { gostrings[i] = C.GoString(s) } return gostrings}
C.gostring's Official document, really concise: (
// C string to Go stringfunc C.GoString(*C.char) string
Reference: https://golang.org/cmd/cgo/
CGO Wiki:https://github.com/golang/go/wiki/cgo
Google Big God more: https://groups.google.com/forum/#!topic/golang-nuts/Nb-nfVdAyF0