cgo 結構體指標 cgo講義及練習

2021-10-13 09:22:47 字數 3408 閱讀 8610

前提條件:

了解go語言和c語言的基本知識和基本用法。

一、什麼是cgo

簡單地說,cgo是在go語言中使用c語言**的一種方式。

二、為什麼要有cgo

c語言經過數十年發展,經久不衰,各個方面的開源**、閉源庫已經非常豐富。這無疑是一塊巨大的寶藏,對於一門現代程式語言而言,如何用好現成的c**就顯得極為重要。

三、如何使用

3.1 系統配置

要想使用cgo,你的計算機上必須有gcc,並且將gcc編譯器的可執行檔案所在的目錄新增到path這個環境變數中。例如,我的gcc.exe在c:\mingw64\bin下,所以,要把c:\mingw64\bin這個目錄新增到path。

3.2 c假包

我們知道,go語言以包為**的邏輯單元。如果要在go**中使用c**,也要為c**單獨設立乙個「包」並將其匯入:

import "c"

c是乙個假包,包的性質它一般也有。例如可以用「包名.符號名」的方式使用其中的變數或型別。

var n c.int

這行**,定義了乙個c語言int型別的變數,與用

var conn net.conn

定義乙個net.conn型別的變數沒什麼語法上的不同。

如果緊挨著import "c"這行上方,加入連續若干行注釋,在注釋中編寫c**,這些c**就作為c包的內容。例如:

int plusone(int n)

return n + 1;

import "c"

在go**中就可以呼叫plusone這個函式,再例如:

#include 

import "c"

在go**中就可以呼叫標頭檔案stdio.h中的函式。

除此之外,還可以把你的c原始檔放到要使用它的go原始檔的同一目錄,然後在c包中包含(include)對應的標頭檔案。例如,我有c原始檔ys_origin.c和標頭檔案ys_origin.h,而我要在ys_origin.go中呼叫ys_origin.c中的函式,那麼,我可以這麼做:

include "ys_origin.h"

import "c"

func funcone(a int, b string) error ;

import "c"

func testarray()  point_beta;

import "c"

func teststruct() 

var pb c.point_beta

pb.x = 33

pb.y = -10

fmt.println(pb) // 

4.6 聯合體

go中使用c的聯合體是比較少見的,而且稍顯麻煩,因為go將c的聯合體視為位元組陣列。比方說,下面的聯合體large_integer被視為[8]byte。

typedef long long;

typedef unsigned long dword;

typedef long long longlong;

typedef union _large_integer  u;

longlong quadpart;

} large_integer, *plarge_integer;

所以,如果乙個c的函式的某個引數的型別為large_integer,我們可以給它乙個[8]byte型別的實參,反之亦然。

那麼,如果乙個c函式要求傳入乙個聯合體,我們應該構建乙個位元組陣列作為實參。

typedef long long;

typedef unsigned long dword;

typedef long long longlong;

typedef union _large_integer  u;

longlong quadpart;

} large_integer, *plarge_integer;

void aaa(large_integer li)

li.u.lowpart = 1;

li.u.highpart = 4;

import "c"

func testunion() ,返回值都是乙個uintptr。它們雖然接受inte***ce{}型別的引數,但必須傳遞乙個go函式,而且傳入的go函式的返回值的大小(size)必須和uintptr相同。它們根據乙個go函式(記憶體中的一段資料),生成乙個c函式(記憶體中的另一段資料),並將這個c函式的首位址返回。兩者的不同點是,前者生成的c函式是符合__stdcall呼叫約定的,後者生成的c函式是符合__cdecl呼叫約定的。

在獲得函式的首位址之後,還不能直接把它傳給c函式,因為c的指向函式的指標在go中被視為*[0]byte,所以要轉換一下。

c**:

#include 

#ifndef null

#define null ((void*)0)

#endif

typedef uintptr_t(__stdcall* girl_proc)(unsigned int);

typedef uintptr_t(__cdecl* girl_proc_cdecl)(unsigned int);

unsigned int func1(unsigned int n, girl_proc gp)

if (gp == null)

return 0;

return (unsigned int)((*gp)(n));

unsigned int func2(unsigned int n, girl_proc_cdecl gp)

if (gp == null)

return 0;

return (unsigned int)((*gp)(n));

go**:

func testcallback() {

f1 := syscall.newcallback(plusone)

f2 := syscall.newcallbackcdecl(plustwo)

var m uint32 = 20

var n uint32 = 80

// func1 __stdcall

fmt.println(c.func1(c.uint(m), (*[0]byte)(unsafe.pointer(f1)))) // 21

// func2 __cdecl

fmt.println(c.func2(c.uint(n), (*[0]byte)(unsafe.pointer(f2)))) // 82

func plusone(n uint32) uintptr {

return uintptr(n + 1)

func plustwo(n uint32) uintptr {

return uintptr(n + 2)

c.func1的第二個引數型別為函式,所以要傳入乙個*[0]byte。

五、綜合示例

正在寫。

六、練習

以後我會製作一些習題。

go語言 結構體及結構體指標

結構體 struct 是由一系列具有相同型別或不同型別的資料構成的資料集合,也叫結構。可以理解為其他程式語言中的類 結構體既可以定義在函式內,也可以定義在函式外,函式外為全域性結構體可以跨包訪問 前提是結構體首字母大寫 結構體變數是值型別,可以使用fmt.printf p 結構體變數 來檢視其記憶體...

C語言瘋狂講義 (十一)C語言動態記憶體及結構體

1 動態記憶體管理 記憶體分為5大區域 棧 存放區域性變數 堆 存放程式執行過程中,動態申請的空間 bss 未初始化的全部變數,和靜態變數 資料段 已經初始化的全域性變數和靜態變數常量 段 程式源 編譯生成的二進位制檔案 1 malloc void malloc size 動態的向記憶體申請連續的s...

C 結構體指標的定義及使用詳解

在解析c 結構體指標前,必須知道c 結構體是如何定義的。在c 中同樣定義該結構體。c 結構體指標之c 結構體的定義 structlayout layoutkind.sequential public struct vgastat 定義完結構體後,就可將接收到的c 結構體指標轉換為定義的結構體物件。v...