C和Go相互呼叫
C可以呼叫Go,並且Go可以呼叫C, 如果更進一步呢, C-->Go-->C
或者 Go-->C-->Go
的呼叫如何實現?
本文通過兩個簡單的例子幫助你瞭解這兩種複雜的呼叫關係。本文不涉及兩者之間的複雜的資料轉換,官方文章C? Go? Cgo!、wiki/cgo和cmd/cgo有一些介紹。
Go–>C–>Go
Go程式呼叫C實現的函式,然後C實現的函式又呼叫Go實現的函式。
1、首先,我們新建一個hello.go
的檔案:
1package main
2import "C"
3import "fmt"
4//export HelloFromGo
5func HelloFromGo() {
6 fmt.Printf("Hello from Go!
")
7}
它定義了一個HelloFromGo
函式,注意這個函式是一個純的Go函式,我們定義它的輸出符號為HelloFromGo
。
2、接著我們新建一個hello.c
的檔案:
1#include <stdio.h>
2#include "_cgo_export.h"
3int helloFromC() {
4 printf("Hi from C
");
5 //call Go function
6 HelloFromGo();
7 return 0;
8}
這個c檔案定義了一個C函式helloFromC
,內部它會呼叫我們剛才定義的HelloFromGo
函式。
這樣,我們實現了C
呼叫Go
: C-->Go
,下面我們再實現Go呼叫C。
3、最後新建一個main.go
檔案:
1package main
2/*
3extern int helloFromC();
4*/
5import "C"
6func main() {
7 //call c function
8 C.helloFromC()
9}
它呼叫第二步實現的C函式helloFromC
。
執行測試一下:
1$ go run .
2Hi from C
3Hello from Go!
可以看到,期望的函式呼叫正常的執行。第一行是C函式的輸出,第二行是Go函式的輸出。
C–>Go–>C
第二個例子演示了C程式呼叫Go實現的函式,然後Go實現的函式又呼叫C實現的函式。
1、首先新建一個hello.c
檔案:
1#include <stdio.h>
2int helloFromC() {
3 printf("Hi from C
");
4 return 0;
5}
它定義了一個純C實現的函式。
2、接著新建一個hello.go
檔案:
1// go build -o hello.so -buildmode=c-shared .
2package main
3/*
4extern int helloFromC();
5*/
6import "C"
7import "fmt"
8//export HelloFromGo
9func HelloFromGo() {
10 fmt.Printf("Hello from Go!
")
11 C.helloFromC()
12}
13func main() {
14}
它實現了一個Go函式HelloFromGo
,內部實現呼叫了C實現的函式helloFromC
,這樣我們就實現了Go-->C
。
注意包名設定為package main
,並且增加一個空的main
函式。
執行go build -o hello.so -buildmode=c-shared .
生成一個C可以呼叫的庫,這調命令執行完後會生成hello.so
檔案和hello.h
檔案。
3、最後新建一個資料夾,隨便起個名字,比如main
將剛才生成的hello.so
檔案和hello.h
檔案複製到main
資料夾,並在main
資料夾中新建一個檔案main.c
:
1#include <stdio.h>
2#include "hello.h"
3int main() {
4 printf("use hello lib from C:
");
5
6 HelloFromGo();
7
8 return 0;
9}
執行gcc -o main main.c hello.so
生成可執行檔案main
, 執行main
:
1$ ./main
2use hello lib from C:
3Hello from Go!
4Hi from C
第一行輸出來自main.c
,第二行來自Go函式,第三行來自hello.c
中的C函式,這樣我們就實現了C-->Go--C
的複雜呼叫。
C-->Go-->C
的狀態變數
我們來分析第二步中的一個特殊的場b景, 為了下面我們好區分,我們給程式標記一下, 記為C1-->Go-->C2
, C2的程式修改一下,加入一個狀態變數a
,並且函式helloFromC
中會列印a
的地址和值,也會將a
加一。
1#include <stdio.h>
2int a = 1;
3int helloFromC() {
4 printf("Hi from C: %p, %d
", &a, a++);
5 return 0;
6}
然後修改main.c
程式,讓它既通過Go嗲用C1.helloFromC
,又直接呼叫C1.helloFromC
,看看多次呼叫的時候a
的指標是否一致,並且a
的值是否有變化。
1#include <stdio.h>
2#include "hello.h"
3int main() {
4 printf("use hello lib from C:
");
5
6 // 1. 直接呼叫C函式
7 helloFromC();
8 // 2. 呼叫Go函式
9 HelloFromGo();
10
11 // 3. 直接呼叫C函式
12 helloFromC();
13 return 0;
14}
激動人心的時候到了。我們不同的編譯方式會產生不同的結果。
1、gcc -o main main.c hello.so
和第二步相同的編譯方式,編譯出main
並執行, 因為hello.so
中包含C1.helloFromC
實現,所以可以正常執行。
1./main
2use hello lib from C:
3Hi from C: 0x10092a370, 1
4Hello from Go!
5Hi from C: 0x10092a370, 2
6Hi from C: 0x10092a370, 3
可以看到a
的指標是同一個值,無論通過Go函式改變還是通過C函式改變都是更改的同一個變數。
nm可以檢視生成的main
的符號:
1nm main
2 U _HelloFromGo
30000000100000000 T __mh_execute_header
4 U _helloFromC
50000000100000f10 T _main
6 U _printf
7 U dyld_stub_binder
U
代表這個符號是未定義的符號,通過動態庫連結進來。
2、 gcc -o main main.c hello.so ../hello.c
我們編譯的時候直接連結hello.c
的實現,然後執行main
:
1./main
2use hello lib from C:
3Hi from C: 0x104888020, 1
4Hello from Go!
5Hi from C: 0x1049f7370, 1
6Hi from C: 0x104888020, 2
可以看到a
是不同的兩個變數。
nm可以檢視生成的main
的符號:
1nm main
2 U _HelloFromGo
30000000100000000 T __mh_execute_header
40000000100001020 D _a
50000000100000f10 T _helloFromC
60000000100000ec0 T _main
7 U _printf
8 U dyld_stub_binder
可以看到_a
是初始化的環境變數,_helloFromC
的型別是T
而不是U
,代表它是一個全域性的Text符號,這和上一步是不一樣的。
參考文件
● https://medium.com/using-go-in-mobile-apps/using-go-in-mobile-apps-part-1-calling-go-functions-from-c-be1ecf7dfbc6
● https://github.com/vladimirvivien/go-cshared-examples
● http://golang.org/cmd/cgo
● https://gist.github.com/zchee/b9c99695463d8902cd33
● https://medium.com/@liamkelly17/working-with-packed-c-structs-in-cgo-224a0a3b708b
● https://groups.google.com/forum/#!topic/golang-nuts/EhndTzcPJxQ
● https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit#
● https://www.mkssoftware.com/docs/man1/nm.1.asp
原文釋出時間為:2018-11-1
本文作者:smallnest
本文來自雲棲社群合作伙伴“Golang語言社群”,瞭解相關資訊可以關注“Golang語言社群”。
相關文章
- go語言與c語言的相互呼叫GoC語言
- python和c++的相互呼叫教程PythonC++
- c與c++的相互呼叫C++
- webview js和java相互呼叫WebViewJSJava
- js和vue方法的相互呼叫(iframe父子頁面的方法相互呼叫)。JSVue
- CXX庫提供Rust和C+ 之間的安全相互呼叫Rust
- 原生實現C#和Lua相互呼叫-Unity3D可用C#Unity3D
- Android JNI開發系列之Java與C相互呼叫AndroidJava
- 組合語言-019(彙編程式與c\c++相互呼叫)組合語言C++
- WebAssembly實踐指南——C++和Rust透過wasmtime實現相互呼叫例項WebC++RustASM
- OC 與 Swift 相互呼叫Swift
- iframe父子級相互呼叫
- 微服務之間的相互呼叫微服務
- C++ class 和 struct 可以相互繼承嗎C++Struct繼承
- Go kit 呼叫圖解和腦圖Go圖解
- 在GO中呼叫C原始碼#基礎篇1Go原始碼
- springboot整合eureka,服務相互呼叫簡單示例Spring Boot
- JavaB2B2C多使用者商城springboot架構-SpringCloud服務相互呼叫RestTemplateJavaSpring Boot架構GCCloudREST
- Vue--子元件之間相互呼叫及傳值Vue元件
- oc與swift檔案的相互呼叫方式——橋接Swift橋接
- C#中JSON字串和Dictionary字典型別的相互轉換C#JSON字串型別
- C#/VB.NET 實現Word和ODT文件相互轉換C#
- C# 實現 gRPC 服務和呼叫C#RPC
- python和C的如何實現互相呼叫?Python
- 鴻蒙HarmonyOS實戰-Web元件(前端函式和應用側函式相互呼叫)鴻蒙Web元件前端函式
- 【C++】兩個類的相互引用C++
- C++呼叫C介面C++
- Python與Javascript相互呼叫超詳細講解(四)使用PyNode進行Python與Node.js相互呼叫項(cai)目(keng)實(jing)踐(yan)PythonJavaScriptNode.jsAI
- PHP FFI呼叫go,居然比go還快PHPGo
- Android JNI實現Java與C/C++互相呼叫,以及so庫的生成和呼叫(JNI方式呼叫美圖秀秀so)AndroidJavaC++
- Python呼叫C/C++方式PythonC++
- Linux C/C++呼叫mongDBLinuxC++
- (C#)時間戳、DateTime相互轉換C#時間戳
- C#:檔案、byte[]、Stream相互轉換C#
- c++中string類物件和字元陣列之間的相互轉換C++物件字元陣列
- C程式函式呼叫&系統呼叫C程式函式
- C++:建構函式的分類和呼叫C++函式
- C#呼叫PythonC#Python