深入Go语言 - 10

pwescool · · 1661 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本章介绍Go如何调用C代码,以及如何调用动态链接库。

如果你正准备使用Go开发你的程序,或者你正将一个C构建的项目转换成Go项目,请尽量使用Go构建你的项目,而不是偷巧的导入C代码,尽量保持Go项目的纯粹,原因可以查看 cgo 和 Go 语言是两码事 ,文末的参考文档中也有这篇文章的原始英文。

但是,有些情况下,我们不得不使用C代码构建,那么我们就可以使用cgo技术。

Go代码调用C函数

cgo可以让Go代码调用C代码。

C代码被封装进“package C”中,你可以访问C实现的类型 C.size_t 、 变量 C.stdout 和 方法 C.putchar ,即使它们的首字母是小写的。

在代码 import “C” 之前有注释(紧接着这个import),那么这个注释称之为 preamble (序言、开场白)。它可以包含编译C package的头文件:

package main

/*
include
include
include
cgo pkg-config: sqlite3
include
include
include
include”pi.h”
include
cgo CFLAGS: -I${SRCDIR}
cgo LDFLAGS: -L${SRCDIR} -lpi
include “pi.h”
*/ 
import”C” 
import”fmt”

funcmain() { 
    fmt.Println(“计算PI值:”) 
    v := C.GoString(C.calc()) 
    fmt.Println(v) 
}

编译: go build main3.go ,因为动态库和生成的可执行文件 main3 在同一个目录下,没有问题,执行main3:

smallnestMBP:ch9 smallnest$./main3 计算PI值:

31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185

上面这个计算Pi的例子我们将C的字符串转换成Go的字符串。 cgo定义了Go和C之间的类型对应关系。

如果C的struct的字段类型是Go的关键字,如 type , 那么在Go代码中可以在字段前加关键字如 x._type

C中的整数类型已经在包C中定义,如 C.char 、 C.short 、 C.ushort 、 C.int 、 C.uint 、 C.longlong 、 C.float ,不一一列举,请看参考文档1

访问C的 struct 、 union 、 enum 类型需要加类型前缀 struct_ 、 union_ 、 enum_ ,如C.struct_stat

访问C中的类型T的size用 C.sizeofT,如C.sizeofstruct_stat Go不支持C的union的概念,只是把它作为相同长度的字节数组

Go的Struct不能嵌入C的类型

Go的API不应该再暴露C的类型给外部 调用C的函数可以进行多值赋值,一个值作为返回值,一个作为errno 当前不支持C的函数指针

C中参数是固定长度的数组,可以把数组名传递给函数,但是Go代码调用中必须显示地将指针指向数组的第一个元素,如C.f(&C.x[0])

对应的类型转换:

char –> C.char –> byte 
signed char –> C.schar –> int8 
unsigned char –> C.uchar –> uint8 
short int –> C.short –> int16 
short unsigned int –> C.ushort –> uint16 
int –> C.int –> int 
unsigned int –> C.uint –> uint32 
long int –> C.long –> int32 or int64 
long unsigned int –> C.ulong –> uint32 or uint64 
long long int –> C.longlong –> int64 
long long unsigned int –> C.ulonglong –> uint64 
float –> C.float –> float32 
double –> C.double –> float64 
wchar_t –> C.wchar_t –> 
void * -> unsafe.Pointer 

项目 giorgisio/cgo 提供了一些Go调用C代码各种类型的例子。

调用动态链接库

对于Windows环境,Go提供了直接加载动态链接库的方法。 首先syscall包下实现了 LoadDLL 、 FindProc 、 Release 方法,可以加载动态链接库以及得到相应的函数。

另外包 golang.org/x/sys/windows 提供了更多的方法,如 LoadLibrary 、 LoadLibraryEx 、 DLL 、 LazyDLL 等方法和类型。

举个栗子:

h, err := windows.LoadLibrary(“kernel32.dll”) 
iferr !=nil{ 
    abort(“LoadLibrary”, err) 
} 
deferwindows.FreeLibrary(h) 
proc, err := windows.GetProcAddress(h, “GetVersion”) 
iferr !=nil{ 
    abort(“GetProcAddress”, err) 
} 
r, , := syscall.Syscall(uintptr(proc),0,0,0,0) 
major := byte(r) 
minor := uint8(r >>8) 
build := uint16(r >>16) 
print(“windows version “, major,”.”, minor,” (Build “, build,”)\n”)

其它平台我还没有发现官方的调用.so或者.dylib的方法, 但是我看到有第三方的作者写了相应的库,提供类似C中的dlopen和dlsym方法: Runtime dynamic library loader

参考


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:pwescool

查看原文:深入Go语言 - 10

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1661 次点击  
加入收藏 微博
下一篇:编译go1.7beta2
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传