2.3.2 Go 字符串和切片
在CGO生成的_cgo_export.h
头文件中还会为Go语言的字符串、切片、字典、接口和管道等特有的数据类型生成对应的C语言类型:
typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
不过需要注意的是,其中只有字符串和切片在CGO中有一定的使用价值,因为CGO为他们的某些GO语言版本的操作函数生成了C语言版本,因此二者可以在Go调用C语言函数时马上使用;而CGO并未针对其他的类型提供相关的辅助函数,且Go语言特有的内存模型导致我们无法保持这些由Go语言管理的内存指针,所以它们C语言环境并无使用的价值。
在导出的C语言函数中我们可以直接使用Go字符串和切片。假设有以下两个导出函数:
//export helloString
func helloString(s string) {}
//export helloSlice
func helloSlice(s []byte) {}
CGO生成的_cgo_export.h
头文件会包含以下的函数声明:
extern void helloString(GoString p0);
extern void helloSlice(GoSlice p0);
不过需要注意的是,如果使用了GoString类型则会对_cgo_export.h
头文件产生依赖,而这个头文件是动态输出的。
Go1.10针对Go字符串增加了一个_GoString_
预定义类型,可以降低在cgo代码中可能对_cgo_export.h
头文件产生的循环依赖的风险。我们可以调整helloString函数的C语言声明为:
extern void helloString(_GoString_ p0);
因为_GoString_
是预定义类型,我们无法通过此类型直接访问字符串的长度和指针等信息。Go1.10同时也增加了以下两个函数用于获取字符串结构中的长度和指针信息:
size_t _GoStringLen(_GoString_ s);
const char *_GoStringPtr(_GoString_ s);
更严谨的做法是为C语言函数接口定义严格的头文件,然后基于稳定的头文件实现代码。
学员评价