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

huangapple go评论96阅读模式
英文:

How to pass pointer to slice to C function in go

问题

背景:使用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中使用它。

如何做到这一点?我已经搜索和尝试了一段时间,但没有成功。

英文:

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)(&amp;count)); pass []string by using []*C.char. Sample code is like this:

/*
#include &lt;stdio.h&gt;
int f(int *c, char **str) {
	int i;
	printf(&quot;%d\n&quot;, *c);
	for (i = 0; i &lt; *c; i++) {
		printf(&quot;%s\n&quot;, str[i]);
	}
	*c = (*c) + 1;
	return 1;
}
*/
import &quot;C&quot;
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(&amp;c_argc, (**C.char)(&amp;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.

答案1

得分: 6

一个Go切片既在Go中分配,又是一个与C数组不同的数据结构,所以你不能将它传递给C函数(cgo也会阻止你这样做,因为切片包含一个Go指针)。

你需要在C中分配数组,以便在C中操作数组。就像使用C.CString一样,你还需要跟踪在哪里释放外部数组,特别是如果C函数可能分配一个新数组。

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

// 将C数组转换为Go数组,以便我们可以对其进行索引
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 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&lt;&lt;30 - 1]*C.char)(cArray)
for index, value := range strs {
	a[index] = C.CString(value)
}

err := C.f(&amp;c_count, (***C.char)(unsafe.Pointer(&amp;cArray)))

huangapple
  • 本文由 发表于 2017年3月1日 19:29:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/42530538.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定