這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
生命不止,繼續 go go go !!!
幾乎所有的程式設計語言都有C語言的影子,當然golang也不例外。可以看到golang的創始者們與c language有著密切的聯絡。所有,golang和c語言的相互調用也是理所應當。
什麼場合會使用Go與C的互操作呢?
下面的地址給出這樣的答案:http://tonybai.com/2012/09/26/interoperability-between-go-and-c/
1、提升局部代碼效能時,用C替換一些Go代碼。C之於Go,好比彙編之於C。
2、嫌Go記憶體GC效能不足,自己手動管理應用記憶體。
3、實現一些庫的Go Wrapper。比如Oracle提供的C版本OCI,但Oracle並未提供Go版本的以及串連DB的協議細節,因此只能通過封裝C OCI版本的方式以提供Go開發人員使用。
4、Go匯出函數供C開發人員使用(目前這種需求應該很少見)。
Cgo
Cgo enables the creation of Go packages that call C code.
Cgo lets Go packages call C code. Given a Go source file written with some special features, cgo outputs Go and C files that can be combined into a single Go package.
這個不要誤解,cgo不是一個package,我們只需要import “C”就好了。
Package unsafe
順便介紹一下unsafe包。
Package unsafe contains operations that step around the type safety of Go programs.
Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines.
指標類型:
*類型:普通指標,用於傳遞對象地址,不能進行指標運算。
unsafe.Pointer:通用指標類型,用於轉換不同類型的指標,不能進行指標運算。
uintptr:用於指標運算,GC 不把 uintptr 當指標,uintptr 無法持有對象。uintptr 類型的目標會被回收。
unsafe.Pointer 可以和 普通指標 進行相互轉換。
unsafe.Pointer 可以和 uintptr 進行相互轉換。
也就是說 unsafe.Pointer 是橋樑,可以讓任意類型的指標實現相互轉換,也可以將任意類型的指標轉換為 uintptr 進行指標運算。
golang調用C語言
直接上代碼了:
package main// typedef int (*intFunc) ();//// int// bridge_int_func(intFunc f)// {// return f();// }//// int fortytwo()// {// return 42;// }import "C"import "fmt"func main() { f := C.intFunc(C.fortytwo) fmt.Println(int(C.bridge_int_func(f)))}
如果編譯遇到錯誤:
cc1.exe: sorry, unimplemented: 64-bit mode not compiled in
說明你使用的是64的golang,而你使用的32位的MinGW,所以需要下載64位的mingw並配置環境變數。
輸出:
42
來點有難度的:
package main// typedef int (*intFunc) ();//// int// bridge_int_func(intFunc f)// {// return f();// }//// int fortytwo()// {// return 42;// }import "C"import "fmt"func main() { f := C.intFunc(C.fortytwo) fmt.Println(int(C.bridge_int_func(f)))}
是不是有點蒙圈,這跟上面的代碼有什麼區別呢?但是當你編譯的時候:
could not determine kind of name for C.bridge_int_func
could not determine kind of name for C.fortytwo
could not determine kind of name for C.intFunc
切記,在注釋和import”C”之間不能有空行
golang中使用c語言中的資料類型
數實值型別
在Go中可以用如下方式訪問C原生的數實值型別:
C.char,
C.schar (signed char),
C.uchar (unsigned char),
C.short,
C.ushort (unsigned short),
C.int, C.uint (unsigned int),
C.long,
C.ulong (unsigned long),
C.longlong (long long),
C.ulonglong (unsigned long long),
C.float,
C.double
指標類型
原生數實值型別的指標類型可按Go文法在類型前面加上,比如var p *C.int。而void比較特殊,用Go中的unsafe.Pointer表示。任何類型的指標值都可以轉換為unsafe.Pointer類型,而unsafe.Pointer類型值也可以轉換為任意類型的指標值。unsafe.Pointer還可以與uintptr這個類型做相互轉換。由於unsafe.Pointer的指標類型無法做算術操作,轉換為uintptr後可進行算術操作。
* 字串類型*
C語言中並不存在正規的字串類型,在C中用帶結尾’\0’的字元數組來表示字串;而在Go中,string類型是原生類型,因此在兩種語言互操作是勢必要做字串類型的轉換。
通過C.CString函數,我們可以將Go的string類型轉換為C的”字串”類型,再傳給C函數使用。就如我們在本文開篇例子中使用的那樣:
s := “Hello Cgo\n”
cs := C.CString(s)
C.print(cs)
數群組類型
C語言中的數組與Go語言中的數組差異較大,後者是實值型別,而前者與C中的指標大部分場合都可以隨意轉換。目前似乎無法直接顯式的在兩者之間進行轉型,官方文檔也沒有說明。但我們可以通過編寫轉換函式,將C的數群組轉換為Go的Slice(由於Go中數組是實值型別,其大小是靜態,轉換為Slice更為通用一些),下面是一個整型數群組轉換的例子:
package main// int cArray[] = {1, 2, 3, 4, 5, 6, 7};import "C"import "fmt"import "unsafe"func CArrayToGoArray(cArray unsafe.Pointer, size int) (goArray []int) { p := uintptr(cArray) for i := 0; i < size; i++ { j := *(*int)(unsafe.Pointer(p)) goArray = append(goArray, j) p += unsafe.Sizeof(j) } return}func main() { goArray := CArrayToGoArray(unsafe.Pointer(&C.cArray[0]), 7) fmt.Println(goArray)}
在c中調用golang代碼
直接上代碼:
package main/*extern void myprint(int i);void dofoo(void) { int i; for (i=0;i<10;i++) { myprint(i); }}*/import "C"import "fmt"//export myprintfunc myprint(i C.int) { fmt.Printf("i = %v\n", uint32(i))}func DoFoo() { C.dofoo()}func main() { DoFoo()}
很遺憾,出現了錯誤:
C:\Users\wangs\AppData\Local\Temp\go-build603641814\cgo_obj\main.cgo2.o: In function dofoo':
D:/go_workspace/src/cgo/main.go:6: multiple definition of
dofoo’
C:\Users\wangs\AppData\Local\Temp\go-build603641814\cgo_obj_cgo_export.o:D:/go_workspace/src/cgo/main.go:6: first defined here
collect2.exe: error: ld returned 1 exit status
修改,拆分成兩個檔案:
main.go
package main/*extern void myprint(int i);void dofoo(void) { int i; for (i=0;i<10;i++) { myprint(i); }}*/import "C"func main() { C.dofoo()}
foo.go:
package mainimport "C"import "fmt"//export myprintfunc myprint(i C.int) { fmt.Printf("i = %v\n", uint32(i))}
參考連結:
http://tonybai.com/2012/09/26/interoperability-between-go-and-c/