C和Go相互調用

來源:互聯網
上載者:User

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的檔案:

hello.go
123456789
package mainimport "C"import "fmt"//export HelloFromGofunc HelloFromGo() {fmt.Printf("Hello from Go!\n")}

它定義了一個HelloFromGo函數,注意這個函數是一個純的Go函數,我們定義它的輸出符號為HelloFromGo

2、接著我們建立一個hello.c的檔案:

hello.c
123456789
#include <stdio.h>#include "_cgo_export.h"int helloFromC() {    printf("Hi from C\n");    //call Go function    HelloFromGo();    return 0;}

這個c檔案定義了一個C函數helloFromC,內部它會調用我們剛才定義的HelloFromGo函數。

這樣,我們實現了C調用Go: C-->Go,下面我們再實現Go調用C。

3、最後建立一個main.go檔案:

main.go
1234567891011
package main/*extern int helloFromC();*/import "C"func main() {//call c functionC.helloFromC()}

它調用第二步實現的C函數helloFromC

運行測試一下:

123
$ go run .Hi from CHello from Go!

可以看到,期望的函數調用正常的運行。第一行是C函數的輸出,第二行是Go函數的輸出。

C-->Go-->C

第二個例子示範了C程式調用Go實現的函數,然後Go實現的函數又調用C實現的函數。

1、首先建立一個hello.c檔案:

hello.c
123456
#include <stdio.h>int helloFromC() {    printf("Hi from C\n");    return 0;}

它定義了一個純C實現的函數。

2、接著建立一個hello.go檔案:

12345678910111213141516171819
// go build -o hello.so -buildmode=c-shared .package main/*extern int helloFromC();*/import "C"import "fmt"//export HelloFromGofunc HelloFromGo() {fmt.Printf("Hello from Go!\n")C.helloFromC()}func main() {}

它實現了一個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:

main.c
12345678910
#include <stdio.h>#include "hello.h"int main() {    printf("use hello lib from C:\n");       HelloFromGo();        return 0;}

運行gcc -o main main.c hello.so產生可執行檔main, 運行main:

1234
$ ./mainuse hello lib from C:Hello from Go!Hi from C

第一行輸出來自main.c,第二行來自Go函數,第三行來自hello.c中的C函數,這樣我們就實現了C-->Go--C的複雜調用。

C-->Go-->C的狀態變數

我們來分析第二步中的一個特殊的情境, 為了下面我們好區分,我們給程式標記一下, 記為C1-->Go-->C2, C2的程式修改一下,加入一個狀態變數a,並且函數helloFromC中會列印a的地址和值,也會將a加一。

hello.c
12345678
#include <stdio.h>int  a = 1;int helloFromC() {    printf("Hi from C: %p, %d\n", &a, a++);    return 0;}

然後修改main.c程式,讓它既通過Go嗲用C1.helloFromC,又直接調用C1.helloFromC,看看多次調用的時候a的指標是否一致,並且a的值是否有變化。

main.c
1234567891011121314151617
#include <stdio.h>#include "hello.h"int main() {    printf("use hello lib from C:\n");       // 1. 直接調用C函數    helloFromC();    // 2. 調用Go函數    HelloFromGo();        // 3. 直接調用C函數    helloFromC();    return 0;}

激動人心的時候到了。我們不同的編譯方式會產生不同的結果。

1、gcc -o main main.c hello.so

和第二步相同的編譯方式,編譯出main並執行, 因為hello.so中包含C1.helloFromC實現,所以可以正常執行。

123456
./mainuse hello lib from C:Hi from C: 0x10092a370, 1Hello from Go!Hi from C: 0x10092a370, 2Hi from C: 0x10092a370, 3

可以看到a的指標是同一個值,無論通過Go函數改變還是通過C函數改變都是更改的同一個變數。

nm可以查看產生的main的符號:

1234567
nm main                 U _HelloFromGo0000000100000000 T __mh_execute_header                 U _helloFromC0000000100000f10 T _main                 U _printf                 U dyld_stub_binder

U代表這個符號是未定義的符號,通過動態庫連結進來。

2、 gcc -o main main.c hello.so ../hello.c

我們編譯的時候直接連結hello.c的實現,然後運行main:

123456
./mainuse hello lib from C:Hi from C: 0x104888020, 1Hello from Go!Hi from C: 0x1049f7370, 1Hi from C: 0x104888020, 2

可以看到a是不同的兩個變數。

nm可以查看產生的main的符號:

12345678
nm main                 U _HelloFromGo0000000100000000 T __mh_execute_header0000000100001020 D _a0000000100000f10 T _helloFromC0000000100000ec0 T _main                 U _printf                 U dyld_stub_binder

可以看到_a是初始化的環境變數,_helloFromC的類型是T而不是U,代表它是一個全域的Text符號,這和上一步是不一樣的。

參考文檔

  1. https://medium.com/using-go-in-mobile-apps/using-go-in-mobile-apps-part-1-calling-go-functions-from-c-be1ecf7dfbc6
  2. https://github.com/vladimirvivien/go-cshared-examples
  3. http://golang.org/cmd/cgo
  4. https://gist.github.com/zchee/b9c99695463d8902cd33
  5. https://medium.com/@liamkelly17/working-with-packed-c-structs-in-cgo-224a0a3b708b
  6. https://groups.google.com/forum/#!topic/golang-nuts/EhndTzcPJxQ
  7. https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit#
  8. https://www.mkssoftware.com/docs/man1/nm.1.asp
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.