renyuneyun renyuneyun -4 years ago 141
C Question

How to pass pointer to slice to C function

Background: using cgo to call C functions from Golang.

I want to use a C function which has this signature:

int f(int *count, char ***strs)
.
It will modify the data of
count
and
strs
, which is the reason why it uses pointer to them.
The value of
count
is the length of
strs
;
strs
is an array of string; the return value is simply an (boolean) indicator which states whether there is an error or not.

In golang, I can successfully pass and modify
count
by using
C.f((*C.int)(&count))
; pass
[]string
by using
[]*C.char
. Sample code is like this:

/*
#include <stdio.h>
int f(int *c, char **str) {
int i;
printf("%d\n", *c);
for (i = 0; i < *c; i++) {
printf("%s\n", str[i]);
}
*c = (*c) + 1;
return 1;
}
*/
import "C"
func go_f(strs []string) int {
count := len(strs)
c_count := C.int(count)

c_strs := make([]*C.char, count)
for index, value := range strs {
c_strs[index] = C.CString(value)
defer C.free(unsafe.Pointer(c_strs[index]))
}

err := C.f(&c_argc, (**C.char)(&c_argv[0]))
return int(err)
}


As you can see, the C function is currently
int f(int *c, char **str)
, but what I'd like is
int f(int *c, char ***str)
.

This is to say: what I actually want is to enable the modification to the string array (e.g. resize) in C and turn it back to a Go string slice so I can still use it in Go.

How to do this? I've searched and experimented for a while but with no luck.

Answer Source

A Go slice is both allocated in Go, and a different data structure than a C array, so you can't pass it to a C function (cgo will also prevent you from doing this because a slice contains a Go pointer)

You need to allocate the array in C in order to manipulate the array in C. Just like with C.CString, you will also need to track where to free the outer array, especially if the C function may possibly allocate a new array.

cArray := C.malloc(C.size_t(c_count) * C.size_t(unsafe.Sizeof(uintptr(0))))

// convert the C array to a Go Array so we can index it
a := (*[1<<30 - 1]*C.char)(cArray)
for index, value := range strs {
    a[index] = C.CString(value)
}

err := C.f(&c_count, (***C.char)(unsafe.Pointer(&cArray)))
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download