go編譯器有兩套:go自帶的和gccgo。其中gccgo是以gcc爲後端,編譯後的go代碼能夠和
gcc編譯的C/C++代碼集成。go自帶的編譯器中有一個叫cgo的工具,能夠用於在go中集成C
語言庫。
在go中若是須要訪問C語言函數,通常是先將C語言函數包裝成go的包。前面咱們已經講了如何
構建一個包,如今咱們演示如何在包中訪問C語言函數。
咱們如今建立和mypkg/hello功能相似的包:mypkg/hello2。hello2.go的內容以下:
php
-
代碼:
全選
-
package hello2
/*
#include
*/
import "C"
func PrintHello() {
C.puts(C.CString("Hello, world\n"))
}
在這個例子中咱們使用C語言的puts函數輸出結果。因爲C語言不支持UTF8,咱們這裏只輸出
英文字母。這裏的import行用於引入C語言庫,在該指令以前緊挨着的註釋會被看成C語言編譯。
而後,C語言的函數能夠經過加C.前綴的方式來使用,例如:C.puts。
因爲GO語言的不一樣數據類型之間是不能轉換的,所以將go的數據類型傳遞給C語言的數據類型
須要強制轉換。C語言的類型和C語言函數的使用方式相似,也要用C.前綴,例如:C.int、
C.float等。須要注意的是go中字符串和C語言中的字符指針是不一樣的,所以有一個專門的函數
C.CString用於將go字符串轉換爲C語言字符串。
另外,目前cgo不支持從go中訪問C語言中可變參數的函數(例如printf)。若是將上目的puts
換爲printf函數,那麼在編譯的時候可能獲得如下錯誤提示:
-
代碼:
全選
-
unexpected type: ...
其中三個點爲printf函數聲明中表示可變參數的部分。
Makefile文件也要做相應的更新,主要是將GOFILES該爲CGOFILES。CGOFILES對應有
C語言代碼的go文件,用cgo編譯。若是工程中有純go的代碼,則仍是對於GOFILES。改動
後的Makefile以下:
-
代碼:
全選
-
include $(GOROOT)/src/Make.$(GOARCH)
TARG=mypkg/hello2
CGOFILES=hello2.go
include $(GOROOT)/src/Make.pkg
# Simple test programs
%: install %.go
$(GC) $*.go
$(LD) -o $@ $*.$O
go是支持垃圾內存自動回收的。對於C語言函數中申請的內存,也應該有C語言模塊負責回收。
對於C.CString返回的字符串空間由go仍是C語言負責回收還不清楚。
固然,將go中的數組傳遞給C語言也是能夠的。例如,下面的代碼用C語言函數打印數組:
-
代碼:
全選
-
package hello2
/*
#include
int printArray(void *p, int len) {
int *v = (int*)p;
int i;
for(i = 0; i < len; i++) {
printf("v[%d]: %d\n", i, v[i]);
}
return i;
}
*/
import "C"
import "unsafe"
import "fmt"
func PrintHello() {
C.puts(C.CString("Hello, world\n"))
}
func PrintSlice(v []int) {
n := C.printArray(unsafe.Pointer(&v[0]), C.int(len(v)))
fmt.Printf("n = %d\n", int(n))
}
咱們在import "C"前面的註釋中定義了一個printArray函數,用C語言方式打印數組。
而後在PrintSlice中經過C.printArray方式調用,unsafe.Pointer(&v[0])
用於將go中的數組地址轉換爲C語言的void指針,C.int(len(v))指定數組的長度。
而後將函數返回值保存在變量n中,數據類型爲C.int。在輸出n的時候,咱們強制轉換爲
go語言的int類型。
測試函數爲hello2app.go,內容以下:
-
代碼:
全選
-
package main
import "mypkg/hello2"
func main() {
print("hello,")
hello2.PrintHello()
hello2.PrintSlice([]int{ 3, 5, 1})
}
編譯運行後輸出如下結果:
-
代碼:
全選
-
[chai@localhost hello2]$ ./hello2app
hello,Hello, world
v[0]: 3
v[1]: 5
v[2]: 1
n = 3
若是咱們須要在C語言函數中分配空間,而後在go中以數組的方式來訪問,能夠用如下方式:
-
代碼:
全選
-
/*
void* getString() {
return "test string";
}
*/
import "C"
import "unsafe"
func TryGetString
p := (*[100]uint8)(unsafe.Pointer(C.getString()))
fmt.Printf("ss: %v\n", string(p[0:10]))
}
這樣咱們就能夠實現go和C語言之間指針的雙向傳遞了。出了指針以外,go中還能夠訪問C語言的
結構體。例如:
-
代碼:
全選
-
/*
typedef struct {
int a;
} CStruct;
*/
import "C"
import "fmt"
func TryStruct() {
v := C.CStruct{ a:10 }
fmt.Printf("v struct: %v\n", v2)
}
C語言的使用先簡要介紹這麼多,詳細的細節清參考$GOROOT/misc/cgo/gmp/gmp.go:
http://golang.org/misc/cgo/gmp/gmp.go?h=gmp
原文:http://chaishushan.blog.163.com/blog/static/130192897201012710273283/html