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文件的註釋吧.