Go語言中使用C語言函數

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

閱讀全文
類別:Golang 查看評論golang

相關文章
相關標籤/搜索