Golang | CGO => 基础
Contents
最简单的CGO程序 hello cgo
|
|
代码通过
import "C"
语句启用CGO
特性,主函数只是通过 Go 内置的 println 函数输出字符串,其中并没有任何和 CGO 相关的代码。虽然没有调用 CGO 的相关函数,但是 go build 命令会在编译和链接阶段启动 gcc 编译器,这已经是一个完整的 CGO 程序了。
基于 C 标准库函数输出字符串
|
|
上面代码有个缺陷:
- 没有在程序退出前,释放 C.CString 创建的 C 语言字符串, 这样会导致内存泄漏 (Note: 至于为什么要释放在后面说明)
使用自己的 C 函数
|
|
使用 C 源文件调用 C 函数
我们也可以将 SayHello 函数放到当前目录下的一个C语言源文件中(后缀名必须是.c)
- 创建 hello.c 文件,实现 SayHello 函数
- 然后在 Go 源文件的 CGO 部分声明 SayHello 函数进行调用
|
|
|
|
当 SayHello 函数在 C 源文件(hello.c) 中实现以后,我们可以将该 C 源文件打包为静态或动态库文件供 Go 程序调用
- Note: 如果以静态库或动态库的方式引用 SayHello 函数的话,需要将对应的 c 源文件移除当前目录,因为: CGO 在构建程序时,会自动自动构建当前目录下的 c 源文件,从而导致 C 函数名冲突。关于静态库等细节后面再仔细学习。
C 代码的模块化
在编程过程中,抽象和模块化是将复杂问题简化的通用手段。当代码语句变多时,我们可以将相似的代码封装到一个个函数中;当程序中的函数变多时,我们将函数拆分到不同的文件或模块中。而模块化编程的核心是面向程序接口编程(这里的接口并不是Go语言的interface,而是API的概念)。
在前面的例子中,我们抽象一个名为 hello 的模块,模块的全部接口函数都在 hello.h 中定义:
|
|
上面代码中,只有一个SayHello函数的声明。但是作为hello模块的用户来说,就可以放心地使用SayHello函数,而无需关心函数的具体实现。作为SayHello函数的实现者来说,函数的实现只要满足头文件中函数的声明的规范即可。
下面是 SayHello 函数的 C 语言实现,对应:hello.c 文件
|
|
在 hello.c 的文件开头,实现者通过 #include “hello.h” 语句包含 SayHello 函数声明的签名,这样可以保证函数的实现满足模块的外的公开接口
用 Go 重新实现 C 函数
其实CGO不仅仅用于Go语言中调用C语言函数,还可以用于导出Go语言函数给C语言函数调用。在前面的例子中,我们已经抽象一个名为hello的模块,模块的全部接口函数都在hello.h头文件定义:
|
|
现在我们创建一个hello.go文件来用Go语言重新实现C语言接口的SayHello函数:
|
|
然后通过 CGO 的 //export SayHello
指令将Go语言实现的函数SayHello导出为C语言函数。需要注意的是,这里其实有两个版本的SayHello函数:一个Go语言环境的;另一个是C语言环境的。cgo生成的C语言版本SayHello函数最终会通过桥接代码调用Go语言版本的SayHello函数。
通过面向C语言接口的编程技术,我们不仅仅解放了函数的实现者,同时也简化的函数的使用者。现在我们可以将SayHello当作一个标准库的函数使用(和puts函数的使用方式类似):
|
|
面向 C 接口的 Go 编程
在开始的例子中,我们的全部CGO代码都在一个Go文件中。然后,通过面向C接口编程的技术将SayHello分别拆分到不同的C文件,而main依然是Go文件。再然后,是用Go函数重新实现了C语言接口的SayHello函数。但是对于目前的例子来说只有一个函数,要拆分到三个不同的文件确实有些繁琐了。
正所谓合久必分、分久必合,我们现在尝试将例子中的几个文件重新合并到一个Go文件。下面是合并后的成果:
|
|
上面代码中 Go 的导出函数 SayHello 参数类型还是使用的 Go 中 C 的原生数据类型。 但是如果可以直接用 Go 的 string 类型则是最直接的。在 Go 1.10 中 CGO 增加了一个 GoString 预定义的 C 语言类型,用来表示 Go 语言字符串。下面是改进后的代码:
|
|
Note: 上面代码虽然看起来都是 Go 语言代码,但是执行的时候还是从 Go 的 main 函数开始执行,到 CGO 自动生成的 C 语言版本的 SayHello 桥接函数,最后又回到了Go语言环境的SayHello函数。这个代码包含了CGO编程的精华。
思考题: main函数和SayHello函数是否在同一个Goroutine只执行?
Click here to checkout the Repo
See Also
Thanks to the authors 🙂