[Go语言]cgo用法演示

经历了数十年发展的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)的方法。

  1. 变量
  2. 函数
  3. 结构体
  4. 联合体
  5. 回调函数
  6. 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
}

6. dll

以后再写。

时间: 2024-12-28 09:14:58

[Go语言]cgo用法演示的相关文章

标准SQL语言的用法

原文链接:http://www.ifyao.com/2015/05/18/%E6%A0%87%E5%87%86%E7%9A%84sql%E8%AF%AD%E8%A8%80%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E5%8F%8A%E5%A4%9A%E8%A1%A8%E8%BF%9E%E6%8E%A5/ 标准SQL语言的用法 SQL语言是目前最通用的关系数据库语言.ANSI SQL是指由美国国家标准局(ANSI)的数据库委员会制定的标准SQL语言,多数关系数据库产品

【转】话说C语言const用法

原文:话说C语言const用法 const在C语言中算是一个比较新的描述符,我们称之为常量修饰符,意即其所修饰的对象为常量(immutable). 我们来分情况看语法上它该如何被使用. 1.函数体内修饰局部变量.例:void func(){const int a=0;} 首先,我们先把const这个单词忽略不看,那么a是一个int类型的局部自动变量,我们给它赋予初始值0. 然后再看const. const作为一个类型限定词,和int有相同的地位.const int a;int const a;是

.net Core MongoDB用法演示

C#驱动MongoDB的本质是将C#的操作代码转换为mongo shell,驱动的API也比较简单明了,方法名和js shell的方法名基本都保持一致,熟悉mongo shell后学习MongoDB的C#驱动是十分轻松的,直接看几个demo吧. 0.准备测试数据 使用js shell添加一些测试数据,如下: use myDb db.userinfos.insertMany([ {_id:1, name: "张三", age: 23,level:10, ename: { firstnam

delphi webbrowser 经常用法演示样例

var Form : IHTMLFormElement ; D:IHTMLDocument2 ; begin with WebBrowser1 do begin D := Document as IHTMLDocument2; Form := D.Forms.item( 'form1 ',0) as IHTMLFormElement; //form1为表单名 //title为表单中的文本框 (form.item( 'title ',0) as IHTMLElement).setAttribute

列表框空间JList的用法演示

package 列表框控件演示; import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.BorderFactory; imp

话说C语言const用法

const在C语言中算是一个比较新的描述符,我们称之为常量修饰符,意即其所修饰 的对象为常量(immutable). 我们来分情况看语法上它该如何被使用. 1.函数体内修饰局部变量. 例: void func(){ const int a=0; } 首先,我们先把const这个单词忽略不看,那么a是一个int类型的局部自动变量, 我们给它赋予初始值0. 然后再看const. const作为一个类型限定词,和int有相同的地位. const int a; int const a; 是等价的.于是此

php之CI框架多语言的用法

public function index() { // 加载语言包,可以加载多个 $this->lang->load('email'); echo $this->lang->line('email_must_be_array')."<br>"; // 加载语言辅助函数,必须先加载完语言包 $this->load->helper('language'); echo lang('email_must_be_array').'<br&g

C语言sscanf用法解析与正则表达式支持

最近学习算法和输入输出用到的基本知识,首先是我自己写的一份代码参考和学习了很多资源 后面会给出参考资料,他们写得更加详细,正则表达式的支持确实是一大亮点所在 #include<iostream> #include<string> #include<cstdio> using namespace std; //字符与其他类型转换函数学习 int main() { //打印到字符串中 cout << "打印到字符串中的技巧\n"; char

C语言 sscanf用法详解

/* sscanf用法详解 */ #include <stdio.h> /* sscanf头文件 */ #include <stdlib.h> #include <string.h> /* sscanf 读取格式化的字符串中的数据. swscanf 是 sscanf 的宽字符版本:swscanf 的参数是宽字符串. swscanf不处理 Unicode 全角十六进制或"兼容性区"字符. 除此以外,swscanf 和 sscanf 的行为完全相同. 函