【问题标题】:Cgo pointer passing rule for pointer string slice (*[]string)?指针字符串切片(* []字符串)的Cgo指针传递规则?
【发布时间】:2019-01-24 05:43:30
【问题描述】:

我可以将 *[]string 从 Go 传递给 C,然后将 append 传递给字符串切片,还是违反了 pointer passing spec

Go 代码可以将 Go 指针传递给 C,前提是它指向的 Go 内存不包含任何 Go 指针。

示例代码:

package main

/*
extern void go_callback(void*, char*);

static inline void callback(void* stringSliceGoPointer) {
    go_callback(stringSliceGoPointer, "foobar");
}
*/
import "C"

import (
    "fmt"
    "unsafe"
)

func main() {
    a := make([]string, 0)
    C.callback(unsafe.Pointer(&a)) 
    fmt.Println(a[0]) // outputs foobar
}

//export go_callback
func go_callback(stringSliceGoPointer unsafe.Pointer, msg *C.char) {
    slice := (*[]string)(stringSliceGoPointer)
    *slice = append(*slice, C.GoString(msg))
}

【问题讨论】:

    标签: pointers go cgo


    【解决方案1】:

    不,这不可能。

    请参阅this 以获取有关 go 数据类型的进一步说明。

    Go 中的字符串类型基本上是这样的。

    str := "hello"
    

    这是存储为,

     str:                0xad234e3b:
     ┌──────────┬─┐      ┌───┬───┬───┬───┬───┐
     |0xad234e3b|5|  ┌──>|104|101|108|108|111| -->[5]byte
     └────┬─────┴─┘  |   └───┴───┴───┴───┴───┘
          └──────────┘
    

    考虑一个切片:

    arr := string{"hi!","hello"}
    

    Further Slice 数据类型包含指针、长度、容量。

    arr:                   0xd2b564c7:        0xad234e40:
    ┌──────────┬─┬─┐       ┌──────────┬─┐     ┌───┬───┬──┐
    |0xd2b564c7|2|2|  ┌──> |0xad234e40|3|────>|104|105|33| -->[3]byte
    └────┬─────┴─┴─┘  |    ├──────────┼─┤     └───┴───┴──┘
         └────────────┘    |0xad234e4b|5|──┐  0xad234e4b:
                           └──────────┴─┘  |  ┌───┬───┬───┬───┬───┐
                                           └─>|104|101|108|108|111| -->[5]byte
                                              └───┴───┴───┴───┴───┘
    

    十六进制值代表地址。

    存储实际数据的位置是[x]byte 的数组。

    x代表数据(数组)的大小。

    很明显[]string 本身包含许多(x) 指针,而*[]string 是另外一个指针。

    【讨论】:

    • 意思是[]string 真的只是[][]byte 这是一个字节片,然后转换成一个数组?所以它是指向违反规范的指针的指针?
    • 对不起,我没有写完整的答案,发错了,我还在写答案。
    【解决方案2】:

    不允许将*[]string 传递给C,因为指向的内存包含字符串,而字符串包含指针。正如the cgo docs 所说(强调我的)

    请注意,某些 Go 类型的值,除了类型的零值, 始终包含 Go 指针。字符串、切片、接口、通道、映射和函数类型都是如此。

    解决这个问题的一种方法是更间接地引用[]string,这样只有 Go 代码才真正知道它的地址。例如:

    package main
    
    /*
    extern void go_callback(int, char*);
    
    static inline void callback(int stringSliceRef) {
        go_callback(stringSliceRef, "foobar");
    }
    */
    import "C"
    
    import (
        "fmt"
    )
    
    // If you need to use these values concurrently,
    // you'll need more code to protect this.
    var stringSlices = make([][]string, 10)
    
    func main() {
        C.callback(0) 
        fmt.Println(stringSlices[0][0]) // outputs foobar
    }
    
    //export go_callback
    func go_callback(ref C.int, msg *C.char) {
        i := int(ref)
        stringSlices[i] = append(stringSlices[i], C.GoString(msg))
    }
    

    【讨论】:

    • 接受这个答案,因为它为问题提供了可能的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    相关资源
    最近更新 更多