如何在Go中将[][]byte转换为**char

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

How to convert from [][]byte to **char in go

问题

我想将一个go [][]byte转换为C **char。换句话说,我有一个go中的字节矩阵,我想将其转换为C中的char双指针。

请假设我必须将[][]byte作为输入和**char作为输出。

我知道可以通过以下方式将[]byte转换为*char:

((*C.char)(unsafe.Pointer(&data[0])))

但是似乎无法将此情况扩展到第二维。我尝试了一种相当复杂的方法,将[][]byte打包到一个新的[]byte中。然后,我将该[]byte发送到一个C函数,该函数使用指针算术在新的[]byte中指向正确位置的地方创建**char。

然而,这种转换给我带来了奇怪的行为,我的数据在几次迭代中是正确的,但在函数调用之间似乎被破坏了。

如果有人有任何想法,我会非常感激。

从我看到的回答中,还很重要的是声明我正在处理原始数据而不是字符串。因此使用了go的byte类型。如果添加C字符串终止符,原始数据将被破坏。我只是使用C **char,因为char的大小为一个字节。话虽如此,感谢回答。我能够根据接受的答案来适应我的需求。

英文:

I would like to convert from a go [][]byte to a C **char. In other words, I have a byte matrix in go that I would like to convert to a char double pointer in C.

Please assume that I HAVE to have a [][]byte as input and a **char as output.

I know it is possible to convert from []byte to *char by doing something like:

((*C.char)(unsafe.Pointer(&data[0])))

But it does not seem possible to extend this case into the second dimension. I have tried something pretty elaborate, where I pack a [][]byte into a new []byte. I then send that []byte to a C function that creates a **char using pointer arithmetic to point into the new []byte at the correct locations.

This conversion is giving me strange behaviour though, where my data would be correct for a few iterations, but gets corrupted seemingly between function calls.

If anyone has any ideas, I would really appreciate it.

From the responses I see it is also important to state that I'm working with raw data and not strings. Hence the go byte type. The original data would, therefore, be corrupted if C string terminators are added. I'm just using C **char, because a char is one byte in size. That said, thanks for the responses. I was able to adapt the accepted answer for my needs.

答案1

得分: 4

未经测试的骨架:

func foo(b [][]byte) {
        outer := make([]*C.char, len(b)+1)
        for i, inner := range b {
                outer[i] = C.CString(string(inner))
        }
        C.bar(unsafe.Pointer(&outer[0])) // void bar(**char) {...}
}

编辑:完整示例(已测试):

package main

/*
#include <stdlib.h>
#include <stdio.h>

void bar(char **a) {
        char *s        ;
        for (;(s = *a++);)
                printf("\"%s\"\n", s);
}
*/
import "C"
import "unsafe"

func foo(b [][]byte) {
        outer := make([]*C.char, len(b)+1)
        for i, inner := range b {
                outer[i] = C.CString(string(inner))
        }
        C.bar((**C.char)(unsafe.Pointer(&outer[0]))) // void bar(**char) {...}
}

func main() {
        foo([][]byte{[]byte("Hello"), []byte("world")})
}

(15:24) jnml@fsc-r550:~/src/tmp/SO/14833531$ go run main.go 
"Hello"
"world"
(15:25) jnml@fsc-r550:~/src/tmp/SO/14833531$
英文:

Untested skeleton:

func foo(b [][]byte) {
        outer := make([]*C.char, len(b)+1)
        for i, inner := range b {
                outer[i] = C.CString(string(inner))
        }
        C.bar(unsafe.Pointer(&amp;outer[0])) // void bar(**char) {...}
}

EDIT: Full example (tested):

package main

/*
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;

void bar(char **a) {
        char *s        ;
        for (;(s = *a++);)
                printf(&quot;\&quot;%s\&quot;\n&quot;, s);
}
*/
import &quot;C&quot;
import &quot;unsafe&quot;

func foo(b [][]byte) {
        outer := make([]*C.char, len(b)+1)
        for i, inner := range b {
                outer[i] = C.CString(string(inner))
        }
        C.bar((**C.char)(unsafe.Pointer(&amp;outer[0]))) // void bar(**char) {...}
}

func main() {
        foo([][]byte{[]byte(&quot;Hello&quot;), []byte(&quot;world&quot;)})
}

(15:24) jnml@fsc-r550:~/src/tmp/SO/14833531$ go run main.go 
&quot;Hello&quot;
&quot;world&quot;
(15:25) jnml@fsc-r550:~/src/tmp/SO/14833531$ 

答案2

得分: 3

这必须手动完成。您必须分配一个新的**C.char类型,并循环遍历[][]byte切片中的每个元素,将其分配给新列表。这涉及到在每次迭代中通过正确的大小偏移**C.char指针。

以下是一个执行此操作的示例程序。

如下面的注释所建议的那样,如果您打算使用类似于C中的printf来打印char *列表,请确保输入字符串以NULL结尾。最好使用C.CString()函数将它们转换。这假设它们要被视为字符串。否则,您可能还需要提供一种方法将每个单独的char *列表的长度传递给C函数。

package main

/*
#include <stdlib.h>
#include <stdio.h>

void test(char **list, size_t len)
{
	size_t i;

	for (i = 0; i < len; i++) {
		//printf("%ld: %s\n", i, list[i]);
	}
}
*/
import "C"
import "unsafe"

func main() {
	list := [][]byte{
		[]byte("foo"),
		[]byte("bar"),
		[]byte("baz"),
	}

	test(list)
}

func test(list [][]byte) {
	// Determine the size of a pointer on the current system.
	var b *C.char
	ptrSize := unsafe.Sizeof(b)

	// Allocate the char** list.
	ptr := C.malloc(C.size_t(len(list)) * C.size_t(ptrSize))
	defer C.free(ptr)

	// Assign each byte slice to its appropriate offset.
	for i := 0; i < len(list); i++ {
		element := (**C.char)(unsafe.Pointer(uintptr(ptr) + uintptr(i)*ptrSize))
		*element = (*C.char)(unsafe.Pointer(&list[i][0]))
	}

	// Call our C function.
	C.test((**C.char)(ptr), C.size_t(len(list)))
}

输出如下:

$ go run charlist.go 
0: foo
1: bar
2: baz
英文:

This has to be done manually. You have to allocate a new **C.char type and loop through each element in the [][]byte slice to assign it to the new list. This involves offsetting the **C.char pointer by the correct size for each iteration.

Here is an example program which does this.

As the comments below suggest, if you intend to print the char * lists using something like printf in C, then ensure the input strings are NULL terminated. Ideally by converting them using the C.CString() function. This assumes that they are to be treated as strings though. Otherwise you may also need to supply a way to pass the length of each individual char * list to the C function.

package main

/*
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;

void test(char **list, size_t len)
{
	size_t i;

	for (i = 0; i &lt; len; i++) {
		//printf(&quot;%ld: %s\n&quot;, i, list[i]);
	}
}
*/
import &quot;C&quot;
import &quot;unsafe&quot;

func main() {
	list := [][]byte{
		[]byte(&quot;foo&quot;),
		[]byte(&quot;bar&quot;),
		[]byte(&quot;baz&quot;),
	}

	test(list)
}

func test(list [][]byte) {
	// Determine the size of a pointer on the current system.
	var b *C.char
	ptrSize := unsafe.Sizeof(b)

	// Allocate the char** list.
	ptr := C.malloc(C.size_t(len(list)) * C.size_t(ptrSize))
	defer C.free(ptr)

	// Assign each byte slice to its appropriate offset.
	for i := 0; i &lt; len(list); i++ {
		element := (**C.char)(unsafe.Pointer(uintptr(ptr) + uintptr(i)*ptrSize))
		*element = (*C.char)(unsafe.Pointer(&amp;list[i][0]))
	}

	// Call our C function.
	C.test((**C.char)(ptr), C.size_t(len(list)))
}

The output is as follows:

$ go run charlist.go 
0: foo
1: bar
2: baz

huangapple
  • 本文由 发表于 2013年2月12日 21:20:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/14833531.html
匿名

发表评论

匿名网友

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

确定