這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在很多情境下,在Go的程式中需要調用c函數或者是用c編寫的庫(底層驅動,演算法等,不想用Go語言再去造一遍輪子,複用現有的c庫)。
那麼該如何調用呢?Go可是更好的C語言啊,當然提供了和c語言互動的功能,稱為Cgo!
Cgo封裝了#cgo偽c文法,參數CFLAGS用來傳入編譯選項,LDFLAGS來導入連結選項。這個用來調用非c標準的第三方c庫。
1)先從最簡單的寫起吧,Go代碼直接調用c函數,下面的樣本中在代碼註解區塊調用了標準的c庫,並寫了一個c函數(本例只是簡單列印了一句話,在該註解區塊中可以寫任意合法的c代碼),在Go代碼部分直接調用該c函數hi()
package mainimport "fmt"/*#include <stdio.h>void hi() { printf("hello world!\n");}*/import "C" //這裡可看作封裝的偽包C, 這條語句要緊挨著上面的註解區塊,不可在它倆之間間隔空行!func main() { C.hi() fmt.Println("Hi, vim-go")}
運行結果:
root@slave2:/home/cgo# go run main.go hello world!Hi, vim-go
好,我可以在Go代碼中寫c代碼了,那麼我該如何在Go中直接調用已經編譯好的第三方c庫呢?用Cgo!
2)本例示範在Go代碼中調用非標準的c的第三方動態庫
c檔案
/* * hi.c * created on: July 1, 2017 * author: mark */#include <stdio.h>void hi() { printf("Hello Cgo!\n");}
h檔案
void hi();
編譯成動態庫.so
root@slave2:/home/cgo# gcc -c -fPIC -o hi.o hi.croot@slave2:/home/cgo# gcc -shared -o libhi.so hi.o
Go檔案
package mainimport "fmt"/*#cgo CFLAGS: -I./#cgo LDFLAGS: -L./ -lhi#include "hi.h" //非標準c標頭檔,所以用引號*/import "C"func main() { C.hi() fmt.Println("Hi, vim-go")}
重點來了(敲黑板):
CFLAGS中的-I(大寫的i) 參數表示.h標頭檔所在的路徑
LDFLAGS中的-L(大寫) 表示.so檔案所在的路徑 -l(小寫L) 表示指定該路徑下的庫名稱,比如要使用libhi.so,則只需用-lhi (省略了libhi.so中的lib和.so字元,關於這些字元所代表的具體含義請自行google)表示。
運行結果:
root@slave2:/home/cgo# go run main.go Hello Cgo!Hi, vim-go
更深入一些,
1)標頭檔路徑和庫檔案路徑寫死的話,一旦第三方庫的安裝路徑變化了,Golang的代碼也要跟著變化,就會很麻煩。這時可以使用cgo命令中使用pk-config,具體請參考這篇博文:Golang使用pkg-config自動擷取標頭檔和連結庫的方法
2)當在Go中使用了以上的方法後,就要求主機(或者雲端服務器)上必須有相應的.so檔案,如果不存在就會連結報錯,導致程式退出。
若.so是一些不必要的第三方驅動庫(可有可無),那就麻煩了,你不能為了跑這個程式,把每台主機都裝上那個不必要的第三方庫吧。有沒有一種方法可以在Go程式運行時才調用這些.so庫呢,如果不存在忽略就好(就不啟用那個庫提供的功能了,而不是連結報錯直接異常退出)?當然有!敬請期待一下一篇。:)