Fork me on GitHub
hahaya +
facebook send mail to hahaya sina weibo
关于订阅 hahaya's blog » Blog » blog
分类:
标签:

今天上班的时候突然想到一个问题:c语言中的char*该怎么转换成go中的string呢?在go语言交流群中问了一下,得到了一个答案是使用cgo。之前只听过有cgo这么个东西,但是没有真正使用过。今天突然想到了这个问题,就抽出时间学习下cgo,接下来就有了这篇文章。

一、介绍

go有强烈的c背景,除了语法具有继承性外,其设计者以及设计目标都和c有着很大的关系。在go和c语言相互操作方面,go提供了强大的支持。尤其是在go中使用c,甚至可以在go源文件中编写c代码,这是其他语言所无法达到的。
在如下一些场景中,可能涉及到go与c的相互操作:

二、go中调用c代码

go中调用c代码会使用到cgo,cgo让go可以非常简单的融合c代码。在import “C”上面添加特殊注释来包含c相关代码,特别注意import “C”和注释的c代码之间一定不能有空行,否则会报错。下面给出了一个小例子:

package main

/*
#include <stdio.h>
#include <stdlib.h>

void output(char *str) {
    printf("%s\n", str);
}
*/
import "C" //此处和上面的C代码一定不能有空行 否则会报错

import "unsafe"

func main() {
    str := "hello cgo"
    //change to char*
    cstr := C.CString(str)
    C.output(cstr)
    C.free(unsafe.Pointer(cstr))
}

这个例子中,与正常的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提供的命令行下编译,同样也得不到输出。直接在终端才编译才得到了正确输出。

作者:hahaya
出处:http://hahaya.github.com/c-and-go
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
分类: 标签:
点击查看评论