C語言?Go語言?還是CGO語言!
Cgo讓Go包呼叫C程式碼。給出一個Go原始檔,用一些特殊功能編寫,cgo輸出Go和C檔案,可以組合成一個Go包。
為了與以身作則,這裡有一個go包,提供了兩個功能- Random
和Seed
-那套C'S 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))的}
我們來看看這裡發生了什麼,從import語句開始。
該rand
包匯入"C"
,但您會發現標準Go庫中沒有這樣的包。這是因為C
該rand
包包含四個包的引用C
:對C.random
和C.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
#cgo
指令用於在構建包的C部分時為編譯器和連結器提供標誌。
有一個限制:如果你的程式使用任何//export
指令,那麼註釋中的C程式碼只能包含宣告(extern int f();
),而不是定義(int
f() { return 1; }
)。您可以使用//export
指令使Go函式可訪問C程式碼。
在#cgo
和//export
指令都記錄中CGO檔案。
字串和東西
與Go不同,C沒有顯式的字串型別。C中的字串由零終止的字元陣列表示。
圍棋和C字串之間的轉換與完成C.CString
,C.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
build
或go install
照常使用。go工具識別特殊"C"
匯入,並自動使用這些檔案的cgo。
更多的cgo資源
的CGO命令檔案具有關於C偽包和構建過程的更多細節。該CGO例子中轉到樹演示更高階的概念。
最後,如果您對所有內容的內部操作感到好奇,請檢視執行時包cgocall.go的介紹性註釋。