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

以后再写。

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

  1. 标准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 ...

  2. HTML5 Canvas阴影用法演示

    HTML5 Canvas阴影用法演示 HTML5 Canvas中提供了设置阴影的四个属性值分别为: context.shadowColor = “red” 表示设置阴影颜色为红色 context.sh ...

  3. 【转】话说C语言const用法

    原文:话说C语言const用法 const在C语言中算是一个比较新的描述符,我们称之为常量修饰符,意即其所修饰的对象为常量(immutable). 我们来分情况看语法上它该如何被使用. 1.函数体内修 ...

  4. C#中的yield return用法演示源码

    下边代码段是关于C#中的yield return用法演示的代码. using System;using System.Collections;using System.Collections.Gene ...

  5. 【三支火把】---C语言const用法总结

    C语言关键字const相信对于不少C语言新手是既陌生又熟悉的,好像经常见,但是却不知道为何用,怎么用?学习至此,总结一下const的用法,使用程序来帮助你理解该关键字,希望能帮到像我一样的新手. 我看 ...

  6. java struts2入门学习--OGNL语言基本用法

    一.知识点学习 1.struts2中包含以下6种对象,requestMap,sessionMap,applicationMap,paramtersMap,attr,valueStack; 1)requ ...

  7. 快速掌握mongoDB(四)—— C#驱动MongoDB用法演示

    前边我们已经使用mongo shell进行增删查改和聚合操作,这一篇简单介绍如何使用C#驱动MongoDB.C#驱动MongoDB的本质是将C#的操作代码转换为mongo shell,驱动的API也比 ...

  8. .net Core MongoDB用法演示

    C#驱动MongoDB的本质是将C#的操作代码转换为mongo shell,驱动的API也比较简单明了,方法名和js shell的方法名基本都保持一致,熟悉mongo shell后学习MongoDB的 ...

  9. 【C_Language】---C语言const用法总结

    C语言关键字const相信对于不少C语言新手是既陌生又熟悉的,好像经常见,但是却不知道为何用,怎么用?学习至此,总结一下const的用法,使用程序来帮助你理解该关键字,希望能帮到像我一样的新手. 我看 ...

随机推荐

  1. 数组转xls格式的excel文件&数据转csv格式的excle

    /** * 数组转xls格式的excel文件 * @param array $data 需要生成excel文件的数组 * @param string $filename 生成的excel文件名 * 示 ...

  2. [CSP-S模拟测试48]反思+题解

    状态很垃圾的一场考试.感觉“这么多分就够了”的心态很是在给自己拖后腿. 打开题面,第一页赫然写着:$Claris' Contest$. 吓得我差点手一抖关掉.不过后来想想似乎强到变态的人出的题都不是很 ...

  3. IE11“__doPostBack”未定义”

    IE 11 下<asp:LinkButton> 点击出现 “__doPostBack”未定义” 在项目根目录 App_Browsers 下新建 浏览器文件(.browser),让其与IE1 ...

  4. 框架-.NET:.NET Core

    ylbtech-框架-.NET:.NET Core .NET Core是适用于 windows.linux 和 macos 操作系统的免费.开源托管的计算机软件框架,是微软开发的第一个官方版本,具有跨 ...

  5. Opengl 之 窗口初体验 ------ By YDD的铁皮锅

    大二的时候开始想着做游戏,因为学校的课程实在是无聊就想着做些有意义的事情.毕竟学了编程这一行就得做些实事,于是就在网上搜了一下图形编程,偶然的了解到了Opengl (同时还有Windows上的Dire ...

  6. servlet项目找不到java文件(由于照抄代码造成的bug)

    其实这个bug非常简单,实际上是由于路径配置问题,也是由于我照抄代码造成的(好孩子不要学!) 情况是这样的:java文件没有问题但是启动项目的时候出现了bug: 然后当我把GetData.java和J ...

  7. POI教程

    很多时候,一个软件应用程序需要生成Microsoft Excel文件格式的报告.有时,一个应用程序甚至希望将Excel文件作为输入数据.例如,一个公司开发的应用程序将财务部门需要所有输出生成自己的Ex ...

  8. 通过一条很慢的SQL梳理下SQL优化基础

  9. 洛谷 P4178 Tree

    #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #inclu ...

  10. npm 安装扩展模块时,因缓存报错的问题汇总

    1.缓存报错问题一 :    unexpected end of file 解决方法:运行:npm cache verify  清除缓存 2.缓存报错问题二 :   errno -4048(网上一般说 ...