天天看點

go與c互相調用目錄結構 go檔案 c語言檔案 編譯生成.a檔案 利用.a檔案 結語

此例子來自于go源碼中,借此來和大家分享一下兩者如何調用,網上很多文章語言不詳,也沒有一個完整的測試例子

  1. 目錄結構

首先src 目錄下有

go與c互相調用目錄結構 go檔案 c語言檔案 編譯生成.a檔案 利用.a檔案 結語

testcgowin目錄下:

go與c互相調用目錄結構 go檔案 c語言檔案 編譯生成.a檔案 利用.a檔案 結語

這裡的_obj目錄是cgo生成的

這裡需要展示的是go中如何調用c語言導出函數,以及在c語言中如何調用go的導出函數.

關鍵是cthread.go和cthread_windows.c兩個檔案

  1. go檔案

cthread.go内容:

package ctestcgowin

// extern void doAdd(int, int);

import "C"

import (

"sync"

"fmt"

)

var sum struct {

sync.Mutex

i int

}

//export Add

func Add(x int) {

defer func() {

recover()

}()

sum.Lock()

sum.i += x

sum.Unlock()

var p *int

*p = 2

func TestCthread() {

sum.i = 0

C.doAdd(10, 6)

want := 10 * (10 - 1) / 2 * 6

if sum.i != want {

fmt.Printf("sum=%d, want %d\n", sum.i, want)

}

fmt.Println("want=",want)

這裡的:

// extern void doAdd(int, int);
import "C"
							           

這兩行非常關鍵,必須緊挨着,不能有空行,而且要緊跟着package語句,import要單獨寫

這裡的注釋相當于c語言聲明了一個函數,你用#include當然也可以。遵循的都是c的文法,少個分号都是會報錯的。

然後是下面兩行:

//export Add
func Add(x int) {
           

export Add表示這是go要導出的一個函數,這樣c裡面可以調用。

如果此行注釋删掉,c檔案将會提示找不到Add函數。

cthread_windows.c:18: undefined reference to `Add'

  1. c語言檔案

// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
#include "_cgo_export.h"

__stdcall
static unsigned int
addThread(void *p)
{
int i, max;

   max = *(int*)p;
for(i=0; i<max; i++)
      Add(i);
return 0;
}

void
doAdd(int max, int nthread)
{
enum { MaxThread = 20 };
int i;
   uintptr_t thread_id[MaxThread];

if(nthread > MaxThread)
      nthread = MaxThread;
for(i=0; i<nthread; i++)
      thread_id[i] = _beginthreadex(0, 0, addThread, &max, 0, 0);
for(i=0; i<nthread; i++) {
      WaitForSingleObject((HANDLE)thread_id[i], INFINITE);
      CloseHandle((HANDLE)thread_id[i]);
   }
}
           

我不曉得這個宏WIN32_LEAN_AND_MEAN什麼意思,跟着寫就行了,也沒有去查閱文檔以及代碼。

這裡doAdd是導出函數,addThread不需要導出,是以加了static,

#include "_cgo_export.h"是因為我們需要調用go的導出函數Add,有興趣的可以看看_obj目錄

到此為止,互相之間的關系已經說明白了,當然go與c語言之間的類型轉換,留作以後再說。

  1. 編譯生成.a檔案

接下來如果想要利用這個lib,很簡單,

go與c互相調用目錄結構 go檔案 c語言檔案 編譯生成.a檔案 利用.a檔案 結語

運作cgo生成必要的檔案,然後go install将編譯生成testcgowin.a檔案,此檔案在pkg\windows_386目錄下

  1. 利用.a檔案

直接看testcgowin.go檔案内容即可:

package main
import "testcgowin"


func main(){
   ctestcgowin.TestCthread();
}
           
  1. 結語

c和go互相調用的關鍵都是通過注釋實作的,并且cgo會自己編譯相應的.c檔案,不需要特别說明,隻需要放到相應目錄下即可。

總的來說go為了和c互操作做了很多,雖然沒法像c++那麼友善,但是基本上來收還是很順利的。

繼續閱讀