调用C库函数
相对而言,Go语言 还是一门非常年轻的语言。
虽然发展迅猛,局限性也有——类库不是特别丰富。有些开源项目,例如 x264 ,只有 C 语言版本, Go 重写一遍也不太现实。
好在, Go 提供了一种调用 C 函数的机制—— cgo 。本节以具体的例子演示,如何使用 cgo 调用 C 函数:
被调用库函数
为了演示需要,虚设一个名为 callee
的函数库。函数库只提供一个名为 sayHello
的函数,接口如头文件:
- /**
- * FileName: callee.h
- * Author: Fasion Chan
- * @contact: fasionchan@gmail.com
- * @version: $Id$
- *
- * Description:
- *
- * Changelog:
- *
- **/
- void SayHello();
sayHello
函数只是简单输出 Hello, world!
,实现如下:
- /**
- * FileName: callee.c
- * Author: Fasion Chan
- * @contact: fasionchan@gmail.com
- * @version: $Id$
- *
- * Description:
- *
- * Changelog:
- *
- **/
- #include <stdio.h>
- #include "callee.h"
- void SayHello() {
- printf("Hello, world!\n");
- }
接下来,将上述代码编译成动态库:
- $ gcc -fPIC -c callee.c
- $ ls
- Makefile callee.c callee.h callee.o caller.go
这个命令将 callee.c
源码编译成 callee.o
目标文件。接下来,将目标文件转成成 libcallee.so
动态库文件:
- $ gcc -shared -o libcallee.so callee.o
- $ ls
- Makefile callee.c callee.h callee.o caller.go libcallee.so
调用方
接下来看看如何在 Go 中调用这个动态库。代码如下:
- /**
- * FileName: caller.go
- * Author: Fasion Chan
- * @contact: fasionchan@gmail.com
- * @version: $Id$
- *
- * Description:
- *
- * Changelog:
- *
- **/
- package main
- /*
- #cgo CFLAGS: -I.
- #cgo LDFLAGS: -L. -lcallee
- #include "callee.h"
- */
- import "C"
- import (
- "fmt"
- )
- func main() {
- C.SayHello();
- fmt.Println("Success!")
- }
第 16
行通过 -I
选项指定头文件搜索路径,编译器据此发现头文件 callee.h
。这里 .
表示当前目录,可以根据项目情况设置为其他目录。
第 17
行通过 -L
选项指定动态库搜索路径,编译器据此发现 libcallee.so
。接着,通过 -l
参数链接到该动态库。
第 18
行则是引入头文件,据此编译器知晓 C 函数接口。
第 20
通过 import
关键字引入一个特殊的模块 C
,之后便可访问到所用链接的 C 库函数。第 27
行,调用 sayHello
函数。
警告
第19行与20行之间不能留空行,不然构建失败!
接下来,编译整个程序:
- $ go build caller.go
- $ ls
- Makefile callee.c callee.h callee.o caller caller.go libcallee.so
- $ ./caller
- Hello, world!
- Success!
看到没有,成功调用 sayHello
函数并输出 Hello, world!
!
自动化构建
以上例子包含多个编译步骤,需要执行多个命令。
在程序开发中,经常也是如此。如果每次修改代码后,都需要手工执行这么多命令,那么效率和质量将深受拖累。
我们可以用更自动化的手段进行构建,以 Makefile
为例:
- # FileName: Makefile
- # Author: Fasion Chan
- # @contact: fasionchan@gmail.com
- # @version: $Id$
- #
- # Description:
- #
- # Changelog:
- #
- .DEFAULT_GOAL := run
- callee.o: callee.c
- gcc -fPIC -c callee.c
- libcallee.so: callee.o
- gcc -shared -o $@ $^
- caller: caller.go libcallee.so
- go build caller.go
- clean:
- rm -f caller libcallee.so callee.o
- run: caller
- ./caller
在源码目录准备以上 Makefile
,之后运行:
- $ make
- gcc -fPIC -c callee.c
- gcc -shared -o libcallee.so callee.o
- go build caller.go
- ./caller
- Hello, world!
- Success!
可以看到, make
命令根据 Makefile 定义自动执行编译命令并执行目标程序。
Makefile 更深入的使用方法不在本节的讨论范畴,有兴趣的童鞋自行 Google 。
下一步
订阅更新,获取更多学习资料,请关注我们的 微信公众号 :