1. 程式人生 > >C語言?Go語言?還是CGO語言!

C語言?Go語言?還是CGO語言!

Cgo讓Go包呼叫C程式碼。給出一個Go原始檔,用一些特殊功能編寫,cgo輸出Go和C檔案,可以組合成一個Go包。

為了與以身作則,這裡有一個go包,提供了兩個功能- RandomSeed-那套C'S randomsrandom功能。

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

我們來看看這裡發生了什麼,從import語句開始。

rand包匯入"C",但您會發現標準Go庫中沒有這樣的包。這是因為C

是一個“偽包”,一個由cgo解釋為C名稱空間的引用的特殊名稱。

rand包包含四個包的引用C:對C.randomC.srandom,轉換C.uint(i)import語句的呼叫。

Random函式呼叫標準C庫random函式並返回結果。在C中,random返回C型別的值long,cgo表示為型別C.long。它必須轉換為Go型別,才能使用Go程式外的Go程式碼,使用普通的Go型別轉換:

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

這是一個等效的函式,它使用臨時變數來更明確地說明型別轉換:

func Random()int {
    var r C.long = C.random()
    返回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文件以獲取這些數字型別名稱的完整列表。

我們尚未審查的這個例子的一個細節是import宣告之上的評論。

/ *
#include <stdlib.h>
* /
import“C”

Cgo認識到這個評論。任何從#cgo空格字元開始的行都將被刪除; 這些成為cgo的指令。當編譯包的C部分時,其餘的行用作標題。在這種情況下,這些行只是一個單獨的#include

語句,但它們幾乎可以是任何C程式碼。這些#cgo指令用於在構建包的C部分時為編譯器和連結器提供標誌。

有一個限制:如果你的程式使用任何//export指令,那麼註釋中的C程式碼只能包含宣告(extern int f();),而不是定義(int f() { return 1; })。您可以使用//export指令使Go函式可訪問C程式碼。

#cgo//export指令都記錄中CGO檔案

字串和東西

與Go不同,C沒有顯式的字串型別。C中的字串由零終止的字元陣列表示。

圍棋和C字串之間的轉換與完成C.CStringC.GoString以及C.GoStringN功能。這些轉換會產生字串資料的副本。

下一個示例實現了Print使用庫中的C fputs函式將字串寫入標準輸出的函式stdio

package print

// #include <stdio.h>
// #include <stdlib.h>
import“C”
import "unsafe"
func列印(s字串){ cs:= C.CString(s) C.fputs(cs,(* C.FILE)(C.stdout)) C.free(unsafe.Pointer(CS))}

Go的記憶體管理器不知道由C程式碼建立的記憶體分配。當您使用C.CString(或任何C記憶體分配)建立C字串時,您必須記住通過呼叫完成它才能釋放記憶體C.free

呼叫C.CString返回一個指向char陣列開頭的指標,所以在函式退出之前,我們將它轉​​換為一個unsafe.Pointer釋放記憶體分配C.free。cgo程式中的常見成語是defer在分配後立即釋放(特別是當以下程式碼比單個函式呼叫更復雜時),如以下重寫Print

func列印(s字串){
    cs:= C.CString(s)
    推遲C.free(unsafe.Pointer(cs))
    C.fputs(cs,(* C.FILE)(C.stdout))
}

構建cgo包

要構建cgo包,只需使用go buildgo install照常使用。go工具識別特殊"C"匯入,並自動使用這些檔案的cgo。

更多的cgo資源

CGO命令檔案具有關於C偽包和構建過程的更多細節。該CGO例子中轉到樹演示更高階的概念。

最後,如果您對所有內容的內部操作感到好奇,請檢視執行時包cgocall.go介紹性註釋