golang 调用C语言 Cgo

Cgo 使得Go程序可以调用C代码. cgo读入一个用特别的格式写的Go语言源文件, 输出Go和C程序, 使得C程序能打包到Go语言的程序包中. html

举例说明一下. 下面是一个Go语言包, 包含了两个函数 -- Random 和 Seed -- 是C语言库中random和srandom函数的马甲. golang

    package rand     /*     #include <stdlib.h>     */     import "C"     func Random() int {        return int(C.random())     }     func Seed(i int) {        C.srandom(C.uint(i))     }

咱们来看一下这里都有什么内容. 开始是一个包的导入语句. sql

rand包导入了"C"包, 但你会发如今Go的标准库里没有这个包. 那是由于C是一个"伪包", 一个为cgo引入的特殊的包名, 它是C命名空间的一个引用. 数组

rand 包包含4个到C包的引用: 调用 C.random和C.srandom, 类型转换 C.uint(i)还有引用语句. dom

Random函数调用libc中的random函数, 而后回返结果. 在C中, random返回一个C类型的长整形值, cgo把它轮换为C.long. 这个值必需转换成Go的类型, 才能在Go程序中使用. 使用一个常见的Go类型转换: 函数

    func Random() int {        return int(C.random())     }

这是一个等价的函数, 使用了一个临时变量来进行类型转换: 网站

    func Random() int {        var r C.long = C.random()           return int(r)     }

Seed函数则相反. 它接受一个Go语言的int类型, 转换成C语言的unsigned int类型, 而后传递给C的srandom函数. ui

    func Seed(i int) {        C.srandom(C.uint(i))     }

须要注意的是, cgo中的unsigned int类型写为C.uint; cgo的文档中有完整的类型列表. spa

这个例子中还有一个细节咱们没有说到, 那就是导入语句上面的注释. code

    /*     #include <stdlib.h>     */     import "C"

Cgo能够识别这个注释, 并在编译C语言程序的时候将它看成一个头文件来处理. 在这个例子中, 它只是一个include语句, 然而其实它能够是使用有效的C语言代码. 这个注释必需紧靠在import "C"这个语句的上面, 不能有空行, 就像是文档注释同样.

Strings and things

与Go语言不一样, C语言中没有显式的字符串类型. 字符串在C语言中是一个以0结尾的字符数组.

Go和C语言中的字符串转换是经过C.CString, C.GoString,和C.GoStringN这些函数进行的. 这些转换将获得字符串类型的一个副本.

下一个例子是实现一个Print函数, 它使用C标准库中的fputs函数把一个字符串写到标准输出上:

    package print     // #include <stdio.h>     // #include <stdlib.h>     import "C"     import "unsafe"     func Print(s string) {        cs := C.CString(s)           C.fputs(cs, (*C.FILE)(C.stdout))              C.free(unsafe.Pointer(cs))     }

在C程序中进行的内存分配是不能被Go语言的内存管理器感知的. 当你使用C.CString建立一个C字符串时(或者其它类型的C语言内存分配), 你必需记得在使用完后用C.free来释放它.

调用C.CString将返回一个指向字符数组开始处的指错, 因此在函数退出前咱们把它转换成一个unsafe.Pointer(Go中与C的void 等价的东西), 使用C.free来释放分配的内存. 一个惯用法是在分配内存后紧跟一个defer(特别是当这段代码比较复杂的时候), 这样咱们就有了下面这个Print函数:

    func Print(s string) {        cs := C.CString(s)           defer C.free(unsafe.Pointer(cs))              C.fputs(cs, (*C.FILE)(C.stdout))     }

构建 cgo 包

若是你使用goinstall, 构建cgo包就比较容易了, 只要调用像日常同样使用goinstall命令, 它就能自动识别这个特殊的import "C", 而后自动使用cgo来编译这些文件.

若是你想使用Go的Makefiles来构建, 那在CGOFILES变量中列出那些要用cgo处理的文件, 就像GOFILES变量包含通常的Go源文件同样.

rand包的Makefile能够写成下面这样:

    include $(GOROOT)/src/Make.inc     TARG=goblog/rand     CGOFILES=\         rand.go\     include $(GOROOT)/src/Make.pkg

而后输入gomake开始构建.

更多 cgo 的资源

cgo的文档中包含了关于C伪包的更多详细的说明, 以及构建过程. Go代码树中的cgo的例子给出了更多更高级的用法.

一个简单而又符合Go惯用法的基于cgo的包是Russ Cox写的gosqlite. 而Go语言的网站上也列出了更多的的cgo包.

最后, 若是你对于cgo的内部是怎么运做这个事情感到好奇的话, 去看看运行时包的cgocall.c文件的注释吧.

原文连接:http://blog.golang.org/2011/03/c-go-cgo.html

相关文章
相关标签/搜索