【问题标题】:How to convert from [][]byte to **char in go如何在 go 中从 [][]byte 转换为 **char
【发布时间】:2013-01-27 19:36:56
【问题描述】:

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

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

我知道可以通过执行以下操作将 []byte 转换为 *char:

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

但似乎不可能将这种情况扩展到第二维。我已经尝试了一些非常复杂的方法,我将一个 [][]byte 打包到一个新的 []byte 中。然后我将该 []byte 发送到 C 函数,该函数使用指针算法创建一个 **char 以指向正确位置的新 []byte。

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

如果有人有任何想法,我将不胜感激。

从我看到的回复中,声明我使用的是原始数据而不是字符串也很重要。因此是 go 字节类型。因此,如果添加 C 字符串终止符,原始数据将被破坏。我只是使用 C **char,因为 char 的大小是一个字节。也就是说,感谢您的回复。我能够根据自己的需要调整接受的答案。

【问题讨论】:

    标签: go type-conversion cgo


    【解决方案1】:

    未经测试的骨架:

    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$ 
    

    【讨论】:

    • 你的例子是做什么的?除了你的代码,你能详细说明一下吗?
    • 该示例将 Go [][]byte 转换为以 null 结尾的 C **char,然后在 C 端打印输出。你问的是这个吗?如果没有,请详细说明您的问题。
    【解决方案2】:

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

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

    正如下面的 cmets 建议的那样,如果您打算在 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
    

    【讨论】:

    • 我认为你没有以空值终止 C 字符串,对吗?您的示例有效,但我怀疑您很幸运!
    • 完全正确。 *element = (*C.char)(unsafe.Pointer(&amp;list[i][0])) 这一行最好是 *element = C.CString(string(list[i]))
    • 等等,为什么 C 结构必须为空终止?请不要假设我正在使用字符串。我正在使用原始数据缓冲区。那字符串函数还得用吗?
    • 如果您将 char * 传递给 printf ,就像 @jmt 在示例中所做的那样,它必须以空值终止。一般来说,它当然不会,但我希望你也想要长度,除非每个数据块都是自我描述的或固定长度的。
    • 是的,我正在使用固定大小的数据缓冲区,所以我知道长度。我明白你的意思是将数据打印为你想要空终止它的字符串。然后,我建议如果更改帖子,不如将空终止符添加到其他地方,因为通过添加空终止符以能够打印出原始数据,您正在破坏原始数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-14
    • 1970-01-01
    • 2011-07-22
    • 2012-06-26
    • 2016-10-10
    • 2014-11-22
    相关资源
    最近更新 更多