如何在Go中将指向切片的指针传递给C函数

5

背景:使用cgo从Golang调用C函数。

我想使用具有以下签名的C函数:int f(int *count, char ***strs)。 它将修改countstrs的数据,这就是为什么它使用指向它们的指针的原因。 count的值是strs的长度;strs是一个字符串数组;返回值只是一个(布尔)指示器,说明是否存在错误。

在Golang中,我可以通过使用C.f((*C.int)(&count))成功地传递和修改count;通过使用[]*C.char传递[]string。示例代码如下:

/*
#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)
}

如您所见,目前的C函数为int f(int *c, char **str),但我想要的是int f(int *c, char ***str)
也就是说:我实际上想要在C中启用对字符串数组的修改(例如调整大小),并将其转换回Go字符串切片,以便我仍然可以在Go中使用它。
如何做到这一点?我已经搜索和尝试了一段时间,但没有成功。

1
你确定在主程序中分配的内存可以在C函数中重新分配吗?这可能会导致内存管理方面的差异。 - VolAnd
不要采用三星级编程,而是让函数返回一个 char**。目前函数的返回代码没有任何有意义的作用。 - Lundin
@VolAnd 目前使用 char** 的工作部分是正确的。然而,我不确定如何处理我的目标 (char***)。 - renyuneyun
@Lundin 这只是一个示例... 并不是我的实际代码所做的。 - renyuneyun
1个回答

6

Go语言中的切片是在Go语言中分配的,与C数组不同,因此您无法将其传递给C函数(由于切片包含Go指针,cgo也会阻止您这样做)

为了在C中操作数组,您需要在C中分配数组。就像使用C.CString一样,如果C函数可能分配新数组,则还需要跟踪释放外部数组的位置。

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)))

我仍在尝试如何正确地从a获取go字符串 - 仅使用C.GoString(a [0])无法产生正确的结果。但是...魔数30是做什么用的? - renyuneyun
@renyuneyun:这个“魔数”不是30,而是1<<30-1,即1073741823。这只是一个很大的数组大小,因为数组类型是静态的。 - JimB
谢谢!(是的,我知道那是位运算。)我只是碰巧在很多地方看到那个值,想知道为什么它是2^30而不是其他值。关于获取Go字符串,将“a”重新赋值以获得“cArray”的新指针值会产生成功的结果。 - renyuneyun
@renyuneyun:啊,好的,那么30是因为2^30 - 1是所有架构上可用的最大数组大小。 - JimB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接