• [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

    以后再写。

  • 相关阅读:
    jQuery之第4章 jQuery中的事件和动画
    jQuery之第3章 jQuery中的DOM操作
    jQuery之第2章 jQuery选择器
    输入一组学生的姓名和成绩,根据成绩降序排名。
    抽象类和接口
    pingpong线程输出问题
    sql优化
    [leedcode 242] Valid Anagram
    [leedcode 241] Different Ways to Add Parentheses
    [leedcode 240] Search a 2D Matrix II
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/6507181.html
Copyright © 2020-2023  润新知