最近由于工作原因,需要实现go语言与c语言的相互调用。由于go语言与c语言有着千丝万缕的暧昧关系,两者之间的调用可以通过语言层面实现。下文是对此的总结。
go语言调用c语言
以下为一个简短的例子:
package main // #include <stdio.h> // #include <stdlib.h> /* void print(char *str) { printf("%s\n", str); } */ import "C" import "unsafe" func main() { s := "Hello Cgo" cs := C.CString(s) C.print(cs) C.free(unsafe.pointer(cs)) }
与“正常”的go代码相比,上述代码有几处“特殊”的地方:
在开头的注释中出现了c语言头文件的include字样
在注释中定义了c语言函数print
import了一个名为C的“包”
在main函数中调用了上述定义的c语言函数print
首先,go源码文件中的c语言代码是需要用注释包裹的,就像上面的include头文件以及print函数定义;其次,import "C"这个语句是必须的,而且其与上面的c代码之间不能用空行分隔,必须紧密相连。这里的”C“不是包名,而是一种类似名字空间的概念,或可以理解为伪包,c语言所有语法元素均在该伪包下面;最后,访问c语法元素时都要在其前面加上伪包前缀,比如C.uint和上面代码中的C.print、C.free等。
更详细的在go中调用c语言的用法可以参考Go与C语言的互操作,本文不再一一细述。
在上面的例子中,c语言是内嵌在go代码中的,如果代码量更大更复杂的话,这显然是很不”专业“的。那么,是否可以将c语言代码从go代码中分离出去,单独定义呢?答案是肯定的,可以通过共享库的方式实现。
#cgo
// hello.go package main // #cgo LDFLAGS: -L ./ -lhello // #include <stdio.h> // #include <stdlib.h> // #include "hello.h" import "C" func main() { C.hello() } // hello.c #include "hello.h" void hello() { printf("hello, go\n"); } // hello.h extern void hello();
#cgoLDFLAGS: -L ./ -lhello
因此,只需要把hello.c编译成动态库,再编译go代码,即可在运行go代码的时候调用共享库中的c语言函数。指令如下:
gcc -fPIC -o libhello.so hello.cgo build -o hello./hello
c语言调用go语言
与在go中调用c源码相比,在c中使用go函数的场合较少。因为一般来说,采用高级语言作为粘合剂调用低级语言能充分发挥各自的特点,而用低级语言调用高级语言反而有可能降低低级语言的性能优势,在go中,可以使用”export + 函数名“来导出go函数为c代码所用,看一个简单的例子:
// hello.go package main import "C" import "fmt" // export Go2C func Go2C() { fmt.Println("hello, C") }
go buildgo build -v -x -buildmode=c-shared -o libhello.so hello.go
编译成功后,只需在c代码中引入新生成的头文件及编译时链接动态库即可实现go函数的调用。代码如下:
// hello.c #include <stdio.h> #include "libhello.h" int main() { Go2C(); return 0; }
gcc -o hello -L. -lhello
小结
go build