在go中调用c语言
作者:hahaya
日期:
今天上班的时候突然想到一个问题:c语言中的char*该怎么转换成go中的string呢?在go语言交流群中问了一下,得到了一个答案是使用cgo。之前只听过有cgo这么个东西,但是没有真正使用过。今天突然想到了这个问题,就抽出时间学习下cgo,接下来就有了这篇文章。
一、介绍
go有强烈的c背景,除了语法具有继承性外,其设计者以及设计目标都和c有着很大的关系。在go和c语言相互操作方面,go提供了强大的支持。尤其是在go中使用c,甚至可以在go源文件中编写c代码,这是其他语言所无法达到的。
在如下一些场景中,可能涉及到go与c的相互操作:
- 提升局部代码性能时,用c替代一些go代码。c对于go而言,就像汇编对于c。
- 觉得go的gc(垃圾回收)性能不足,自己动手管理内存。
- 实现一些库的go wrapper。比如Oracle提供了c版本的OCI,但是Oracle并未提供go版本驱动以及连接DB的协议细节。因此只能通过包装C版本的OCI以提供go开发者使用。
- go导出函数供c开发者使用。
- maybe more……
二、go中调用c代码
go中调用c代码会使用到cgo,cgo让go可以非常简单的融合c代码。在import “C”上面添加特殊注释来包含c相关代码,特别注意import “C”和注释的c代码之间一定不能有空行,否则会报错。下面给出了一个小例子:
这个例子中,与正常的go代码相比,有几个不同的位置:
- 注释部分出现了c代码
- import了一个”C”的包
- 在go的main函数中调用了注释中的c函数output
其实这个例子没错,这是go源码中调用c代码的步骤,由此可知在go源码中能直接编写c代码。总结起来go中调用c代码的步骤如下:
- go源码文件中的c代码需要用注释起来,就像上面例子中的include头文件和output函数
- 必须使用import “C”,且这句话和上面注释的c代码之间不能有空行。这里的”C”其实不是包名,而是一种类似名字空间的概念,或可以理解为伪包,c语言的所有语法元素都在该伪包下
- 访问c语法元素时,都要在其前面加上伪包前缀,比如C.output、C.free等等
说了半天,那么上面的代码该如何编译呢?其实与”正常”go源文件没什么区别,依旧可以直接通过go build或go run来编译和执行。但实际编译过程中,go调用了名为cgo的工具,cgo会识别和读取go源文件中的c元素,并将其提取后交给c编译器编译,最后与go源码编译后的目标文件链接成一个可执行程序。这样我们就不难理解为何go源文件中的c代码要用注释包裹了,这些特殊的语法都是可以被cgo识别并使用的。
PS 1:该开始编译这个程序时,由于gcc版本太低,程序能正常编译通过,但是没有任何输出,找了很久,才发现gcc版本要在4.6之上。
PS 2:最好不用在Sublime Text2提供的命令行下编译,同样也得不到输出。直接在终端才编译才得到了正确输出。