Go語言學習之cgo(golang與C語言相互調用)

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

生命不止,繼續 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/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.