GO中调用C代码(CGO)中的坑
背景
网上有很多讲解使用CGO实现GO中调用C代码的博客。总结起来,Go中调用C一共有两种办法:
- 第一种是将C代码直接嵌入到GO源文件中
- 第二种是将C代码写在C文件中,再在GO文件中引入
第一种实现起来比较顺滑,按照已有的博客教程中的步骤来,基本不会出错。笔者在项目中恰好要用到第二种办法,但是按照博客(http://tonybai.com/2012/09/26/interoperability-between-go-and-c/)中的步骤来,始终得不到正确结果(这里并无批评博主Tony Bai的意思,笔者其实从中学到不少)。到网上找了很多介绍CGO使用的博客,都没能解决,几乎要崩溃的时候,从以下帖子https://groups.google.com/forum/#!topic/golang-nuts/O4RTszIyL7c 中得到启示,顺利解决问题。
问题重现
从博客http://tonybai.com/2012/09/26/interoperability-between-go-and-c/中摘取相关代码文件,其代码组织和源文件内容分别如下:
//foo.h
int count;
void foo();
//foo.c
#include <stdio.h>
#include "foo.h"
int count = 6;
void foo() {
printf("I am foo!\n");
}
package main
// #cgo LDFLAGS: -L ./ -lfoo
// #include <stdio.h>
// #include <stdlib.h>
// #include "foo.h"
import "C"
import "fmt“
func main() {
fmt.Println(C.count)
C.foo()
}
按照众多博客中的方法,一般分为两步:
- 将C文件编译成动态链接库libfoo.so
- 使用命令go build foo.go编译生成可执行文件
但在第二步的编译过程中会出现如下“对foo未定义的引用“的错误:
问题解决
我们不需要手动地给go提供动态链接库,甚至于go根本不认识我们自己手动编译出的动态链接库。其实cgo提供了一种机制:它能够根据import”C”中引入的头文件,自动找到相应的源文件进行编译链接。这种机制的调用,需要用到go build命令。
注意:不是go build foo.go命令。go build foo.go只会单独编译foo.go文件,而不会自动编译链接相关的C源文件
因此,我们无需手动编译生成动态库。更进一步,foo.go中的代码也可以进一步精简如下:
package main
// #include <stdio.h>
// #include <stdlib.h>
// #include "foo.h"
import "C"
import "fmt"
func main() {
fmt.Println(C.count)
C.foo()
}
结果如下所示: