指针字符串切片(* [] string)的Cgo指针传递规则?

时间:2018-08-17 08:11:20

标签: pointers go cgo

我可以从Go中将*[]string传递给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))
}

2 个答案:

答案 0 :(得分:3)

不,这不可能。

请参阅this,以进一步了解go数据类型。

基本上,Go中的字符串类型看起来像这样。

str := "hello"

此存储为

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

考虑切片:

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

进一步的切片数据类型包含指针,长度,容量。

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是另外一个指针。

答案 1 :(得分: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))
}