工具 cgo 提供了對(duì) FFI(外部函數(shù)接口)的支持,能夠使用 Go 代碼安全地調(diào)用 C 語(yǔ)言庫(kù),你可以訪問(wèn) cgo 文檔主頁(yè):http://golang.org/cmd/cgo。cgo 會(huì)替代 Go 編譯器來(lái)產(chǎn)生可以組合在同一個(gè)包中的 Go 和 C 代碼。在實(shí)際開(kāi)發(fā)中一般使用 cgo 創(chuàng)建單獨(dú)的 C 代碼包。
如果你想要在你的 Go 程序中使用 cgo,則必須在單獨(dú)的一行使用 import "C"
來(lái)導(dǎo)入,一般來(lái)說(shuō)你可能還需要 import "unsafe"
。
然后,你可以在 import "C"
之前使用注釋(單行或多行注釋均可)的形式導(dǎo)入 C 語(yǔ)言庫(kù)(甚至有效的 C 語(yǔ)言代碼),它們之間沒(méi)有空行,例如:
// #include <stdio.h>
// #include <stdlib.h>
import "C"
名稱 "C" 并不屬于標(biāo)準(zhǔn)庫(kù)的一部分,這只是 cgo 集成的一個(gè)特殊名稱用于引用 C 的命名空間。在這個(gè)命名空間里所包含的 C 類型都可以被使用,例如 C.uint
、C.long
等等,還有 libc 中的函數(shù) C.random()
等也可以被調(diào)用。
當(dāng)你想要使用某個(gè)類型作為 C 中函數(shù)的參數(shù)時(shí),必須將其轉(zhuǎn)換為 C 中的類型,反之亦然,例如:
var i int
C.uint(i) // 從 Go 中的 int 轉(zhuǎn)換為 C 中的無(wú)符號(hào) int
int(C.random()) // 從 C 中 random() 函數(shù)返回的 long 轉(zhuǎn)換為 Go 中的 int
下面的 2 個(gè) Go 函數(shù) Random()
和 Seed()
分別調(diào)用了 C 中的 C.random()
和 C.srandom()
。
示例 3.2 c1.go
package rand
// #include <stdlib.h>
import "C"
func Random() int {
return int(C.random())
}
func Seed(i int) {
C.srandom(C.uint(i))
}
C 當(dāng)中并沒(méi)有明確的字符串類型,如果你想要將一個(gè) string 類型的變量從 Go 轉(zhuǎn)換到 C 時(shí),可以使用 C.CString(s)
;同樣,可以使用 C.GoString(cs)
從 C 轉(zhuǎn)換到 Go 中的 string 類型。
Go 的內(nèi)存管理機(jī)制無(wú)法管理通過(guò) C 代碼分配的內(nèi)存。
開(kāi)發(fā)人員需要通過(guò)手動(dòng)調(diào)用 C.free
來(lái)釋放變量的內(nèi)存:
defer C.free(unsafe.Pointer(Cvariable))
這一行最好緊跟在使用 C 代碼創(chuàng)建某個(gè)變量之后,這樣就不會(huì)忘記釋放內(nèi)存了。下面的代碼展示了如何使用 cgo 創(chuàng)建變量、使用并釋放其內(nèi)存:
示例 3.3 c2.go
package print
// #include <stdio.h>
// #include <stdlib.h>
import "C"
import "unsafe"
func Print(s string) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
C.fputs(cs, (*C.FILE)(C.stdout))
}
構(gòu)建 cgo 包
你可以在使用將會(huì)在第 9.5 節(jié)講到的 Makefile 文件(因?yàn)槲覀兪褂昧艘粋€(gè)獨(dú)立的包),除了使用變量 GOFILES 之外,還需要使用變量 CGOFILES 來(lái)列出需要使用 cgo 編譯的文件列表。例如,示例 3.2 中的代碼就可以使用包含以下內(nèi)容的 Makefile 文件來(lái)編譯,你可以使用 gomake 或 make:
include $(GOROOT)/src/Make.inc
TARG=rand
CGOFILES=\
c1.go\
include $(GOROOT)/src/Make.pkg
SWIG(簡(jiǎn)化封裝器和接口生成器)支持在 Linux 系統(tǒng)下使用 Go 代碼調(diào)用 C 或者 C++ 代碼。這里有一些使用 SWIG 的注意事項(xiàng):
這類接口支持方法重載、多重繼承以及使用 Go 代碼實(shí)現(xiàn) C++ 的抽象類。
目前使用 SWIG 存在的一個(gè)問(wèn)題是它無(wú)法支持所有的 C++ 庫(kù),比如說(shuō)它就無(wú)法解析 TObject.h。