golang 調用C語言 Cgo

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

Cgo 使得Go程式能夠調用C代碼. cgo讀入一個用特別的格式寫的Go語言源檔案, 輸出Go和C程式, 使得C程式能打包到Go語言的程式包中.

舉例說明一下. 下面是一個Go語言套件, 包含了兩個函數 -- Random 和 Seed -- 是C語言庫中random和srandom函數的馬甲.

    package rand    /*    #include <stdlib.h>    */     import "C"     func Random() int {        return int(C.random())     }     func Seed(i int) {        C.srandom(C.uint(i))     }

我們來看一下這裡都有什麼內容. 開始是一個包的匯入語句.

rand包匯入了"C"包, 但你會發現在Go的標準庫裡沒有這個包. 那是因為C是一個"偽包", 一個為cgo引入的特殊的包名, 它是C命名空間的一個引用.

rand 包包含4個到C包的引用: 調用 C.random和C.srandom, 類型轉換 C.uint(i)還有引用語句.

Random函數調用libc中的random函數, 然後回返結果. 在C中, random返回一個C類型的長整形值, cgo把它輪換為C.long. 這個值必需轉換成Go的類型, 才能在Go程式中使用. 使用一個常見的Go類型轉換:

    func Random() int {        return int(C.random())     }

這是一個等價的函數, 使用了一個臨時變數來進行類型轉換:

    func Random() int {        var r C.long = C.random()           return int(r)     }

Seed函數則相反. 它接受一個Go語言的int類型, 轉換成C語言的unsigned int類型, 然後傳遞給C的srandom函數.

    func Seed(i int) {        C.srandom(C.uint(i))     }

需要注意的是, cgo中的unsigned int類型寫為C.uint; cgo的文檔中有完整的類型列表.

這個例子中還有一個細節我們沒有說到, 那就是匯入語句上面的注釋.

    /*    #include <stdlib.h>    */     import "C"

Cgo可以識別這個注釋, 並在編譯C語言程式的時候將它當作一個標頭檔來處理. 在這個例子中, 它只是一個include語句, 然而其實它可以是使用有效C語言代碼. 這個注釋必需緊靠在import "C"這個語句的上面, 不能有空行, 就像是文檔注釋一樣.

Strings and things

與Go語言不同, C語言中沒有顯式的字串類型. 字串在C語言中是一個以0結尾的字元數組.

Go和C語言中的字串轉換是通過C.CString, C.GoString,和C.GoStringN這些函數進行的. 這些轉換將得到字串類型的一個副本.

下一個例子是實現一個Print函數, 它使用C標準庫中的fputs函數把一個字串寫到標準輸出上:

    package print     // #include <stdio.h>     // #include <stdlib.h>     import "C"     import "unsafe"     func Print(s string) {        cs := C.CString(s)           C.fputs(cs, (*C.FILE)(C.stdout))              C.free(unsafe.Pointer(cs))     }

在C程式中進行的記憶體配置是不能被Go語言的記憶體管理器感知的. 當你使用C.CString建立一個C字串時(或者其它類型的C語言記憶體配置), 你必需記得在使用完後用C.free來釋放它.

調用C.CString將返回一個指向字元數組開始處的指錯, 所以在函數退出前我們把它轉換成一個unsafe.Pointer(Go中與C的void 等價的東西), 使用C.free來釋放分配的記憶體. 一個慣用法是在分配記憶體後緊跟一個defer(特別是當這段代碼比較複雜的時候), 這樣我們就有了下面這個Print函數:

    func Print(s string) {        cs := C.CString(s)           defer C.free(unsafe.Pointer(cs))              C.fputs(cs, (*C.FILE)(C.stdout))     }

構建 cgo 包

如果你使用goinstall, 構建cgo包就比較容易了, 只要調用像平常一樣使用goinstall命令, 它就能自動識別這個特殊的import "C", 然後自動使用cgo來編譯這些檔案.

如果你想使用Go的Makefiles來構建, 那在CGOFILES變數中列出那些要用cgo處理的檔案, 就像GOFILES變數包含一般的Go源檔案一樣.

rand包的Makefile可以寫成下面這樣:

    include $(GOROOT)/src/Make.inc    TARG=goblog/rand    CGOFILES=\         rand.go\     include $(GOROOT)/src/Make.pkg

然後輸入gomake開始構建.

更多 cgo 的資源

cgo的文檔中包含了關於C偽包的更多詳細的說明, 以及構建過程. Go代碼樹中的cgo的例子給出了更多更進階的用法.

一個簡單而又符合Go慣用法的基於cgo的包是Russ Cox寫的gosqlite. 而Go語言的網站上也列出了更多的的cgo包.

最後, 如果你對於cgo的內部是怎麼運作這個事情感到好奇的話, 去看看運行時包的cgocall.c檔案的注釋吧.

原文連結:http://blog.golang.org/2011/03/c-go-cgo.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.