作者:林冠巨集 / 指尖下的幽靈
掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8
部落格:http://www.cnblogs.com/linguanh/
GitHub : https://github.com/af913337456/
大部分人學習或者使用某樣東西,喜歡在直觀上看到動手後的結果,才會有繼續下去的興趣。
前言:
Golang 呼叫 C/C++ 的教程網上很多,就我目前所看到的,個人見解就是比較亂,坑也很多。希望本文能在一定程度上,做到更通俗明瞭。
下面 golang 簡稱 go , 一如既往,少說廢話,我們現在開始。
go 呼叫 c/c++ 函式的實現方式有:
- 直接
巢狀
在go檔案中使用,最簡單直觀的 - 匯入
動態庫 .so 或 dll
的形式,最安全但是很不爽也比較慢的 - 直接引用 c/c++ 檔案的形式,層次分明,容易隨時修改看結果的
第三個直接引用 c/c++ 檔案的形式
是我要介紹的重點。
需要的環境支援
- Linux 具備 gcc 與 g++ 即可
- Windows 需要安裝 mingw,否則編譯時會有這類錯:
cannot find -lmingwex
- Mac 參考 Linux
1,直接巢狀在go檔案
package main
/*
// C 標誌io標頭檔案,你也可以使用裡面提供的函式
#include <stdio.h>
void pri(){
printf("hey");
}
int add(int a,int b){
return a+b;
}
*/
import "C" // 切勿換行再寫這個
import "fmt"
func main() {
fmt.Println(C.add(2, 1))
}
複製程式碼
上面的程式碼,直接拷貝執行就能輸出結果:3
結論:
- 但凡要引用與 c/c++ 相關的內容,寫到 go 檔案的頭部
註釋
裡面 - 巢狀的 c/c++ 程式碼必須符合其語法,不與 go 一樣
import "C"
這句話要緊隨,註釋後,不要換行,否則報錯- go 程式碼中呼叫 c/c++ 的格式是:
C.xxx()
,例如 C.add(2, 1)
2,匯入動態庫 .so 或 .dll 的形式
假設專案目錄如下
|-project
| |-lib
| | |-libvideo.dll
| | |-libvideo.so
| |-include
| | |-video.h
| |-src
| | |-main.go
複製程式碼
標頭檔案 .h 如下面這樣
//video.h
#ifndef VIDEO_H
#define VIDEO_H
void exeFFmpegCmd(char* cmd); // 宣告
#endif
複製程式碼
原始檔 .c 如下面這樣
#include <stdio.h>
#include "video.h"
void exeFFmpegCmd(char* cmd){ // 實現
// ....
printf("finish");
}
複製程式碼
使用 gcc 或 g++ 生成 .so庫,或 win 下生成 dll
例如: gcc video.c -fPIC -shared -o libvideo.so
最後 main.go
把動態庫放到一個你喜歡的目錄,也可以放到當前專案裡面,像上面列出的例子一樣。再引用
package main
/*
#cgo CFLAGS: -Iinclude
#cgo LDFLAGS: -Llib -llibvideo
#include "video.h"
*/
import "C"
import "fmt"
func main() {
cmd := C.CString("ffmpeg -i ./xxx/*.png ./xxx/yyy.mp4")
C.exeFFmpegCmd(&cmd)
}
複製程式碼
先回答為什麼說這種是最安全的和最不爽的?原因如下:
- 動態庫破解十分困難,如果你的 go 程式碼洩露,核心動態庫沒那麼容易被攻破
- 動態庫會在被使用的時候被載入,影響速度
- 操作難度比方式一麻煩不少
結論
CFLAGS: -I路徑
這句話指明標頭檔案所在路徑,-Iinclude 指明 當前專案根目錄的 include 資料夾LDFLAGS: -L路徑 -l名字
指明動態庫的所在路徑,-Llib -llibvideo,指明在 lib 下面以及它的名字 video- 如果動態庫不存在,將會爆
找不到定義之類
的錯誤資訊
3,直接引用 c/c++ 檔案的形式 (重點)
假設專案目錄如下
|-util
| |-util.h
| |-util.c
| |-util.go
複製程式碼
util.h
int sum(int a,int b);
複製程式碼
util.c
#include "util.h"
int sum(int a,int b){
return (a+b);
}
複製程式碼
util.go
package util
/*
#include "util.c"
*/
import "C"
import "fmt"
func GoSum(a,b int) {
s := C.sum(C.int(a),C.int(b))
fmt.Println(s)
}
複製程式碼
這樣呼叫 main.go
package main
func main(){
util.GoSum(4,5)
}
複製程式碼
第三種方式便是如此簡潔明瞭
。
最後,補充一下,一般需要 go 呼叫 c/c++ 的,主要是使用一些著名的開源庫,例如 ffmpeg
,opencv
,等這些原始碼是基於 c/c++ 語言的,除此之外還有一個很重要的點,便是執行速度!