go--cgo

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。經曆了數十年發展的C語言,各種各樣的現成的庫已經非常豐富。通過cgo,可以在Go語言中使用C語言代碼,充分利用好現有的“輪子”。
本文所有代碼,在下述環境中調試通過:
    Windows 8.1 64-bit
    Go 1.3.3 64-bit
    GCC 4.8.1 64-bit
要想使用cgo,要匯入C“包”:
import "C"
這行代碼的上方要緊挨著連續的若干行的注釋,在這些注釋中編寫C代碼。例如:
/*
int PlusOne(int n)
{
    return n + 1;
}
*/
import "C"
我們知道,如果要引用一個包中的符號,需要用“包名.符號名”的方式,C“包”也是這樣,例如:C.int、C.GetWindowLongPtr。
下面介紹使用C語言變數、函數、結構體、聯合體、回呼函數和動態連結程式庫(Dynamic Link Library,dll)的方法。
    變數
    函數
    結構體
    聯合體
    回呼函數

    dll


1. 變數


使用C的變數很簡單,比方說,要使用int,只要在Go代碼中寫C.int就可以了。
package main

import (
    "fmt"
)

import "C"

func main() {
    var n C.int
    n = 5
    fmt.Println(n) // 5

    var m1 int
    // Go不認為C.int與int、int32等類型相同
    // 所以必須進行轉換
    m1 = int(n + 3)
    fmt.Println(m1) // 8

    var m2 int32
    m2 = int32(n + 20)
    fmt.Println(m2) // 25
}


2. 函數

在Go中調用C的函數也不困難。

package main

import (
    "fmt"
)

/*
int PlusOne(int n)
{
    return n + 1;
}
*/
import "C"

func main() {
    var n int = 10
    var m int = int(C.PlusOne(C.int(n))) // 類型要轉換
    fmt.Println(m)                       // 11
}


3. 結構體

package main

import (
    "fmt"
)

/*
typedef struct _POINT
{
    double x;
    double y;
}POINT;
*/
import "C"

func main() {
    var p C.POINT
    p.x = 9.45
    p.y = 23.12
    fmt.Println(p) // {9.45 23.12}
}


4. 聯合體

Go中使用C的聯合體是比較少見而奇怪的事情,而且稍顯麻煩,因為Go將C的聯合體視為位元組數組。比方說,下面的聯合體LARGE_INTEGER被視為[8]byte。

typedef long LONG;
typedef unsigned long DWORD;
typedef long long LONGLONG;

typedef union _LARGE_INTEGER {
    struct {
        DWORD LowPart;
        LONG HighPart;
    };
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
    LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;


所以,如果一個C的函數的某個參數的類型為LARGE_INTEGER,我們可以給它一個[8]byte類型的實參,反之亦然。

package main

import (
    "fmt"
)

/*
typedef long LONG;
typedef unsigned long DWORD;
typedef long long LONGLONG;

typedef union _LARGE_INTEGER {
    struct {
        DWORD LowPart;
        LONG HighPart;
    };
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
    LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;

void Show(LARGE_INTEGER li)
{
    li.u.LowPart = 1;
    li.u.HighPart = 4;
}
*/
import "C"

func main() {
    var li C.LARGE_INTEGER // 等價於: var li [8]byte
    var b [8]byte = li     // 正確,因為[8]byte和C.LARGE_INTEGER相同
    C.Show(b)              // 參數類型為LARGE_INTEGER,可以接收[8]byte
    li[0] = 75
    fmt.Println(li) // [75 0 0 0 0 0 0 0]
    li[4] = 23
    Test(li) // 參數類型為[8]byte,可以接收C.LARGE_INTEGER
}

func Test(b [8]byte) {
    fmt.Println(b)
}



5. 回呼函數

有些C函數的參數是回呼函數,比方說:

typedef UINT_PTR(__stdcall* GIRL_PROC)(int);
typedef UINT_PTR(__cdecl* GIRL_PROC_CDECL)(int);

UINT_PTR Func1(int n, GIRL_PROC gp)
{
    if (gp == NULL)
    {
        return 0;
    }
    return (*gp)(n);
}

UINT_PTR Func2(int n, GIRL_PROC_CDECL gp)
{
    if (gp == NULL)
    {
        return 0;
    }
    return (*gp)(n);
}


syscall包中有如下兩個函數:

syscall.NewCallback
syacall.NewCallbackCDecl

其中,第一個函數接收一個Go函數(這個Go函數的傳回值必須只有一個,而且類型為uintptr),並產生一個__stdcall呼叫慣例的C函數,並將產生的函數的地址以uintptr的形式返回;第二個函數的作用與之類似,但產生的函數的呼叫慣例是__cdecl。

一個值得注意的問題是:C的指向函數的指標在Go中被視為*[0]byte,所以要轉換一下才能用。這裡示範一下__stdcall呼叫慣例的函數的用法,__cdecl類似。

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

/*
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

typedef UINT_PTR(__stdcall* GIRL_PROC)(int);
typedef UINT_PTR(__cdecl* GIRL_PROC_CDECL)(int);

UINT_PTR Func1(int n, GIRL_PROC gp)
{
    if (gp == NULL)
    {
        return 0;
    }
    return (*gp)(n);
}

UINT_PTR Func2(int n, GIRL_PROC_CDECL gp)
{
    if (gp == NULL)
    {
        return 0;
    }
    return (*gp)(n);
}
*/
import "C"

func GirlProc(n int32) uintptr {
    return uintptr(n + 97)
}

func main() {
    gp := syscall.NewCallback(GirlProc)
    fmt.Println(gp)
    gop := (*[0]byte)(unsafe.Pointer(gp))
    var t C.UINT_PTR = C.Func1(C.int(29), gop)
    fmt.Println(t) // 126
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.