詳解如何在Go語(yǔ)言中調(diào)用C源代碼
開(kāi)坑說(shuō)明
最近在編寫客戶端程序或與其他部門做功能集成時(shí)多次碰到了跨語(yǔ)言的sdk集成,雖說(shuō)方案很多諸如rpc啊,管道啊,文件io啊,unix socket啊之類的不要太多,但最完美的基礎(chǔ)方式還是讓程序與sdk結(jié)合到一起(個(gè)人觀點(diǎn),不喜勿噴),順便研究了下在go調(diào)用標(biāo)準(zhǔn)c接口的種種方法與坑,內(nèi)容不少,有空便慢慢更新了。
內(nèi)嵌形式
先讓我們來(lái)看一個(gè)最簡(jiǎn)單的cgo實(shí)例
package main
//#include <stdio.h>
import "C"
func main() {
C.puts(C.CString("Hello World"))
}
輸出
Hello World
通過(guò)"C包"調(diào)用了c中常見(jiàn)的puts函數(shù)同時(shí)傳入通過(guò)C.Cstring把go 中string轉(zhuǎn)化為的c string(相當(dāng)于char *)。其實(shí)“C”這個(gè)并不是一個(gè)包,而是通過(guò)import "C"語(yǔ)句啟用了go編譯器cgo相關(guān)的功能讓gcc也參與到了編譯中。這種方式通過(guò)緊貼在import "C"語(yǔ)句上面的注釋中編寫c代碼并在后續(xù)代碼中使用C對(duì)象調(diào)用。當(dāng)然也可以通過(guò)這種方式調(diào)用自定義的c函數(shù)。
package main
import "C"
/*#include <stdio.h>
void say_hello_with_name(char * name){
printf("hello %s\n", name);
}
*/
import "C"
func main() {
C.say_hello_with_name(C.CString("oscar"))
}輸出
hello oscar
外置的C代碼
內(nèi)置的C代碼固然很方便,但用到cgo大多數(shù)的使用場(chǎng)景是我有一個(gè)需要復(fù)用的c代碼庫(kù),像是c++的stl庫(kù)亦或者是linux c中的什么已經(jīng)封裝好的第三方依賴。這些時(shí)候便需要外置一些c的文件.h .c .cpp之類與.go文件混編。先看一個(gè)最簡(jiǎn)單的例子(調(diào)用linux的系統(tǒng)賬戶認(rèn)證)。
// auth.h int auth(char *user, char *passwd);
// auth.c
#include <shadow.h>
#include <stdio.h>
#include <unistd.h>
int auth(char *user, char *passwd){
char *obtpwd;
struct spwd *spasswd;
spasswd = getspnam(user);
obtpwd = crypt(passwd, spasswd->sp_pwdp);
if(strcmp(spasswd->sp_pwdp, obtpwd) == 0)
return 0;
else return 1;
}// main.go
package main
/*
#cgo LDFLAGS: -lcrypt
#include "auth.h"
*/
import "C"
import "fmt"
func main() {
var username, password string
fmt.Println("Please enter your username and password: ")
_, _ = fmt.Scanln(&username, &password)
rst := C.auth(C.CString(username), C.CString(password))
fmt.Println(rst)
}保證上述三個(gè)文件在同一個(gè)go工程目錄下運(yùn)行 go build -o main 構(gòu)建工程。#cgo LDFLAGS: -lcrypt 這個(gè)一行是cgo給gcc的編譯參數(shù),相關(guān)的編譯參數(shù)與連接參數(shù)有空了在后面的文章里說(shuō)明,-lcrypt 表示編譯時(shí)需要去連接libcrypt這個(gè)庫(kù)。
注意,這種c go 混編的方式個(gè)人是不建議的,cgo對(duì)外置c代碼片構(gòu)建支持非常差,我無(wú)法在cgo中通過(guò)編譯參數(shù)指定c代碼片的搜索路徑(頭文件倒是沒(méi)啥問(wèn)題),這也就意味著當(dāng)項(xiàng)目被調(diào)用的c代碼片都得在項(xiàng)目根目錄下,這可太糟糕了。個(gè)人覺(jué)得如果有大量的外部依賴c語(yǔ)言的庫(kù)請(qǐng)分開(kāi)編譯,c庫(kù)使用gcc編譯成靜態(tài)或動(dòng)態(tài)庫(kù)在讓go在編譯時(shí)連接為好,寫個(gè)makefile分開(kāi)分步編譯也不是什么麻煩事,還是上面的例子,讓我們把編譯的過(guò)程稍加修改。
1. 構(gòu)建libauth.a靜態(tài)庫(kù)
gcc -c -o auth.o -lcrypt auth.c ar rcs libauth.a auth.o
得到libauth.a
2. 對(duì)main.go稍加修改
package main
/*
#cgo CFLAGS: -I./
#cgo LDFLAGS: -L. -lauth -lcrypt
#include "auth.h"
*/
import "C"
import "fmt"
func main() {
var username, password string
fmt.Println("Please enter your username and password: ")
_, _ = fmt.Scanln(&username, &password)
rst := C.auth(C.CString(username), C.CString(password))
fmt.Println(rst)
}
此處修改主要是新增了libauth.a靜態(tài)庫(kù)的鏈接參數(shù)
3. 編譯
go build -o main main.go
可以把上述的步驟整下寫個(gè)簡(jiǎn)單的makefile
.PHONY : all all: main libauth.a: auth.c gcc -c -o auth.o -lcrypt auth.c ar rcs libauth.a auth.o main: main.go libauth.a go build -o main main.go clean: rm -f auth.o libauth.a main
這樣也讓我們得出產(chǎn)物的過(guò)程變得相對(duì)簡(jiǎn)單快捷
到此這篇關(guān)于詳解如何在Go語(yǔ)言中調(diào)用C源代碼的文章就介紹到這了,更多相關(guān)Go調(diào)用C源代碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言常見(jiàn)錯(cuò)誤接口污染解決分析
這篇文章主要為大家介紹了Go語(yǔ)言常見(jiàn)錯(cuò)誤接口污染解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
使用Go?goroutine實(shí)現(xiàn)并發(fā)的Clock服務(wù)
這篇文章主要為大家詳細(xì)介紹了如何使用Go?goroutine實(shí)現(xiàn)并發(fā)的Clock服務(wù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-06-06

