詳解如何在Go語言中調(diào)用C源代碼
開坑說明
最近在編寫客戶端程序或與其他部門做功能集成時多次碰到了跨語言的sdk集成,雖說方案很多諸如rpc啊,管道啊,文件io啊,unix socket啊之類的不要太多,但最完美的基礎方式還是讓程序與sdk結(jié)合到一起(個人觀點,不喜勿噴),順便研究了下在go調(diào)用標準c接口的種種方法與坑,內(nèi)容不少,有空便慢慢更新了。
內(nèi)嵌形式
先讓我們來看一個最簡單的cgo實例
package main //#include <stdio.h> import "C" func main() { C.puts(C.CString("Hello World")) }
輸出
Hello World
通過"C包"調(diào)用了c中常見的puts函數(shù)同時傳入通過C.Cstring把go 中string轉(zhuǎn)化為的c string(相當于char *)。其實“C”這個并不是一個包,而是通過import "C"語句啟用了go編譯器cgo相關(guān)的功能讓gcc也參與到了編譯中。這種方式通過緊貼在import "C"語句上面的注釋中編寫c代碼并在后續(xù)代碼中使用C對象調(diào)用。當然也可以通過這種方式調(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ù)的使用場景是我有一個需要復用的c代碼庫,像是c++的stl庫亦或者是linux c中的什么已經(jīng)封裝好的第三方依賴。這些時候便需要外置一些c的文件.h .c .cpp之類與.go文件混編。先看一個最簡單的例子(調(diào)用linux的系統(tǒng)賬戶認證)。
// 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) }
保證上述三個文件在同一個go工程目錄下運行 go build -o main 構(gòu)建工程。#cgo LDFLAGS: -lcrypt 這個一行是cgo給gcc的編譯參數(shù),相關(guān)的編譯參數(shù)與連接參數(shù)有空了在后面的文章里說明,-lcrypt 表示編譯時需要去連接libcrypt這個庫。
注意,這種c go 混編的方式個人是不建議的,cgo對外置c代碼片構(gòu)建支持非常差,我無法在cgo中通過編譯參數(shù)指定c代碼片的搜索路徑(頭文件倒是沒啥問題),這也就意味著當項目被調(diào)用的c代碼片都得在項目根目錄下,這可太糟糕了。個人覺得如果有大量的外部依賴c語言的庫請分開編譯,c庫使用gcc編譯成靜態(tài)或動態(tài)庫在讓go在編譯時連接為好,寫個makefile分開分步編譯也不是什么麻煩事,還是上面的例子,讓我們把編譯的過程稍加修改。
1. 構(gòu)建libauth.a靜態(tài)庫
gcc -c -o auth.o -lcrypt auth.c ar rcs libauth.a auth.o
得到libauth.a
2. 對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)庫的鏈接參數(shù)
3. 編譯
go build -o main main.go
可以把上述的步驟整下寫個簡單的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ān)于詳解如何在Go語言中調(diào)用C源代碼的文章就介紹到這了,更多相關(guān)Go調(diào)用C源代碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang學習筆記之安裝Go1.15版本(win/linux/macos/docker安裝)
這篇文章主要介紹了Golang學習筆記之安裝Go1.15版本(win/linux/macos/docker安裝),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12