在go中调用c语言

作者:hahaya
日期:2013-08-08

今天上班的时候突然想到一个问题: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代码之间一定不能有空行,否则会报错。下面给出了一个小例子:

  1. package main

  2. /*

  3. #include <stdio.h>

  4. #include <stdlib.h>

  5. void output(char *str) {

  6. printf("%s\n", str);

  7. }

  8. */

  9. import "C" //此处和上面的C代码一定不能有空行 否则会报错

  10. import "unsafe"

  11. func main() {

  12. str := "hello cgo"

  13. //change to char*

  14. cstr := C.CString(str)

  15. C.output(cstr)

  16. C.free(unsafe.Pointer(cstr))

  17. }

这个例子中,与正常的go代码相比,有几个不同的位置:

  1. 注释部分出现了c代码
  2. import了一个”C”的包
  3. 在go的main函数中调用了注释中的c函数output

其实这个例子没错,这是go源码中调用c代码的步骤,由此可知在go源码中能直接编写c代码。总结起来go中调用c代码的步骤如下:

  1. go源码文件中的c代码需要用注释起来,就像上面例子中的include头文件和output函数
  2. 必须使用import “C”,且这句话和上面注释的c代码之间不能有空行。这里的”C”其实不是包名,而是一种类似名字空间的概念,或可以理解为伪包,c语言的所有语法元素都在该伪包下
  3. 访问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提供的命令行下编译,同样也得不到输出。直接在终端才编译才得到了正确输出。

<<使用go操作MongoDB数据库 C++使用protobuf(Linux下)>>

首页 - Wiki
Copyright © 2011-2024 iteam. Current version is 2.125.3. UTC+08:00, 2024-05-21 13:31
浙ICP备14020137号-1 $访客地图$