Go语言学习之cgo(golang与C语言相互调用)
2017-08-21 16:10
741 查看
生命不止,继续 go go go !!!
几乎所有的编程语言都有C语言的影子,当然golang也不例外。可以看到golang的创始者们与c language有着密切的联系。所有,golang和c语言的相互调用也是理所应当。
什么场合会使用Go与C的互操作呢?
下面的地址给出这样的答案:http://tonybai.com/2012/09/26/interoperability-between-go-and-c/
1、提升局部代码性能时,用C替换一些Go代码。C之于Go,好比汇编之于C。
2、嫌Go内存GC性能不足,自己手动管理应用内存。
3、实现一些库的Go Wrapper。比如Oracle提供的C版本OCI,但Oracle并未提供Go版本的以及连接DB的协议细节,因此只能通过包装C OCI版本的方式以提供Go开发者使用。
4、Go导出函数供C开发者使用(目前这种需求应该很少见)。
Cgo lets Go packages call C code. Given a Go source file written with some special features, cgo outputs Go and C files that can be combined into a single Go package.
这个不要误解,cgo不是一个package,我们只需要import “C”就好了。
Package unsafe
顺便介绍一下unsafe包。
Package unsafe contains operations that step around the type safety of Go programs.
Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines.
指针类型:
*类型:普通指针,用于传递对象地址,不能进行指针运算。
unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算。
uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收。
unsafe.Pointer 可以和 普通指针 进行相互转换。
unsafe.Pointer 可以和 uintptr 进行相互转换。
也就是说 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。
如果编译遇到错误:
cc1.exe: sorry, unimplemented: 64-bit mode not compiled in
说明你使用的是64的golang,而你使用的32位的MinGW,所以需要下载64位的mingw并配置环境变量。
输出:
42
来点有难度的:
是不是有点蒙圈,这跟上面的代码有什么区别呢?但是当你编译的时候:
could not determine kind of name for C.bridge_int_func
could not determine kind of name for C.fortytwo
could not determine kind of name for C.intFunc
切记,在注释和import”C”之间不能有空行
在Go中可以用如下方式访问C原生的数值类型:
C.char,
C.schar (signed char),
C.uchar (unsigned char),
C.short,
C.ushort (unsigned short),
C.int, C.uint (unsigned int),
C.long,
C.ulong (unsigned long),
C.longlong (long long),
C.ulonglong (unsigned long long),
C.float,
C.double
指针类型
原生数值类型的指针类型可按Go语法在类型前面加上,比如var p *C.int。而void比较特殊,用Go中的unsafe.Pointer表示。任何类型的指针值都可以转换为unsafe.Pointer类型,而unsafe.Pointer类型值也可以转换为任意类型的指针值。unsafe.Pointer还可以与uintptr这个类型做相互转换。由于unsafe.Pointer的指针类型无法做算术操作,转换为uintptr后可进行算术操作。
* 字符串类型*
C语言中并不存在正规的字符串类型,在C中用带结尾’\0’的字符数组来表示字符串;而在Go中,string类型是原生类型,因此在两种语言互操作是势必要做字符串类型的转换。
通过C.CString函数,我们可以将Go的string类型转换为C的”字符串”类型,再传给C函数使用。就如我们在本文开篇例子中使用的那样:
s := “Hello Cgo\n”
cs := C.CString(s)
C.print(cs)
数组类型
C语言中的数组与Go语言中的数组差异较大,后者是值类型,而前者与C中的指针大部分场合都可以随意转换。目前似乎无法直接显式的在两者之间进行转型,官方文档也没有说明。但我们可以通过编写转换函数,将C的数组转换为Go的Slice(由于Go中数组是值类型,其大小是静态的,转换为Slice更为通用一些),下面是一个整型数组转换的例子:
很遗憾,出现了错误:
C:\Users\wangs\AppData\Local\Temp\go-build603641814\cgo_obj\main.cgo2.o: In function
C:\Users\wangs\AppData\Local\Temp\go-build603641814\cgo_obj_cgo_export.o:D:/go_workspace/src/cgo/main.go:6: first defined here
collect2.exe: error: ld returned 1 exit status
修改,拆分成两个文件:
main.go
foo.go:
参考链接:
http://tonybai.com/2012/09/26/interoperability-between-go-and-c/
几乎所有的编程语言都有C语言的影子,当然golang也不例外。可以看到golang的创始者们与c language有着密切的联系。所有,golang和c语言的相互调用也是理所应当。
什么场合会使用Go与C的互操作呢?
下面的地址给出这样的答案:http://tonybai.com/2012/09/26/interoperability-between-go-and-c/
1、提升局部代码性能时,用C替换一些Go代码。C之于Go,好比汇编之于C。
2、嫌Go内存GC性能不足,自己手动管理应用内存。
3、实现一些库的Go Wrapper。比如Oracle提供的C版本OCI,但Oracle并未提供Go版本的以及连接DB的协议细节,因此只能通过包装C OCI版本的方式以提供Go开发者使用。
4、Go导出函数供C开发者使用(目前这种需求应该很少见)。
Cgo
Cgo enables the creation of Go packages that call C code.Cgo lets Go packages call C code. Given a Go source file written with some special features, cgo outputs Go and C files that can be combined into a single Go package.
这个不要误解,cgo不是一个package,我们只需要import “C”就好了。
Package unsafe
顺便介绍一下unsafe包。
Package unsafe contains operations that step around the type safety of Go programs.
Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines.
指针类型:
*类型:普通指针,用于传递对象地址,不能进行指针运算。
unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算。
uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收。
unsafe.Pointer 可以和 普通指针 进行相互转换。
unsafe.Pointer 可以和 uintptr 进行相互转换。
也就是说 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。
golang调用C语言
直接上代码了:package main // typedef int (*intFunc) (); // // int // bridge_int_func(intFunc f) // { // return f(); // } // // int fortytwo() // { // return 42; // } import "C" import "fmt" func main() { f := C.intFunc(C.fortytwo) fmt.Println(int(C.bridge_int_func(f))) }
如果编译遇到错误:
cc1.exe: sorry, unimplemented: 64-bit mode not compiled in
说明你使用的是64的golang,而你使用的32位的MinGW,所以需要下载64位的mingw并配置环境变量。
输出:
42
来点有难度的:
package main // typedef int (*intFunc) (); // // int // bridge_int_func(intFunc f) // { // return f(); // } // // int fortytwo() // { // return 42; // } import "C" import "fmt" func main() { f := C.intFunc(C.fortytwo) fmt.Println(int(C.bridge_int_func(f))) }
是不是有点蒙圈,这跟上面的代码有什么区别呢?但是当你编译的时候:
could not determine kind of name for C.bridge_int_func
could not determine kind of name for C.fortytwo
could not determine kind of name for C.intFunc
切记,在注释和import”C”之间不能有空行
golang中使用c语言中的数据类型
数值类型在Go中可以用如下方式访问C原生的数值类型:
C.char,
C.schar (signed char),
C.uchar (unsigned char),
C.short,
C.ushort (unsigned short),
C.int, C.uint (unsigned int),
C.long,
C.ulong (unsigned long),
C.longlong (long long),
C.ulonglong (unsigned long long),
C.float,
C.double
指针类型
原生数值类型的指针类型可按Go语法在类型前面加上,比如var p *C.int。而void比较特殊,用Go中的unsafe.Pointer表示。任何类型的指针值都可以转换为unsafe.Pointer类型,而unsafe.Pointer类型值也可以转换为任意类型的指针值。unsafe.Pointer还可以与uintptr这个类型做相互转换。由于unsafe.Pointer的指针类型无法做算术操作,转换为uintptr后可进行算术操作。
* 字符串类型*
C语言中并不存在正规的字符串类型,在C中用带结尾’\0’的字符数组来表示字符串;而在Go中,string类型是原生类型,因此在两种语言互操作是势必要做字符串类型的转换。
通过C.CString函数,我们可以将Go的string类型转换为C的”字符串”类型,再传给C函数使用。就如我们在本文开篇例子中使用的那样:
s := “Hello Cgo\n”
cs := C.CString(s)
C.print(cs)
数组类型
C语言中的数组与Go语言中的数组差异较大,后者是值类型,而前者与C中的指针大部分场合都可以随意转换。目前似乎无法直接显式的在两者之间进行转型,官方文档也没有说明。但我们可以通过编写转换函数,将C的数组转换为Go的Slice(由于Go中数组是值类型,其大小是静态的,转换为Slice更为通用一些),下面是一个整型数组转换的例子:
package main // int cArray[] = {1, 2, 3, 4, 5, 6, 7}; import "C" import "fmt" import "unsafe" func CArrayToGoArray(cArray unsafe.Pointer, size int) (goArray []int) { p := uintptr(cArray) for i := 0; i < size; i++ { j := *(*int)(unsafe.Pointer(p)) goArray = append(goArray, j) p += unsafe.Sizeof(j) } return } func main() { goArray := CArrayToGoArray(unsafe.Pointer(&C.cArray[0]), 7) fmt.Println(goArray) }
在c中调用golang代码
直接上代码:package main /* extern void myprint(int i); void dofoo(void) { int i; for (i=0;i<10;i++) { myprint(i); } } */ import "C" import "fmt" //export myprint func myprint(i C.int) { fmt.Printf("i = %v\n", uint32(i)) } func DoFoo() { C.dofoo() } func main() { DoFoo() }
很遗憾,出现了错误:
C:\Users\wangs\AppData\Local\Temp\go-build603641814\cgo_obj\main.cgo2.o: In function
dofoo': D:/go_workspace/src/cgo/main.go:6: multiple definition ofdofoo’
C:\Users\wangs\AppData\Local\Temp\go-build603641814\cgo_obj_cgo_export.o:D:/go_workspace/src/cgo/main.go:6: first defined here
collect2.exe: error: ld returned 1 exit status
修改,拆分成两个文件:
main.go
package main /* extern void myprint(int i); void dofoo(void) { int i; for (i=0;i<10;i++) { myprint(i); } } */ import "C" func main() { C.dofoo() }
foo.go:
package main import "C" import "fmt" //export myprint func myprint(i C.int) { fmt.Printf("i = %v\n", uint32(i)) }
参考链接:
http://tonybai.com/2012/09/26/interoperability-between-go-and-c/
相关文章推荐
- 【GOLANG】Go语言学习-time包
- go语言学习-golang的基本数据类型
- Go语言学习笔记----与C语言的比较学习
- golang学习之旅:搭建go语言开发环境
- go语言学习-golang的基本数据类型
- Golang学习笔记(2)---go语言基本类型
- C语言与汇编语言的相互调用
- 【GOLANG】Go语言学习-logger
- Golang 入门系列(二)学习Go语言需要注意的坑
- Go语言中调用C语言----类型转换篇
- C语言与汇编语言相互调用
- Go语言学习笔记(八)golang 操作 Redis & Mysql & RabbitMQ
- 关于C语言和汇编语言相互嵌套调用
- 【GOLANG】Go语言学习-select用法
- Thrift 的C与golang语言实现以及相互调用方式
- go语言学习-在ubuntu上安装golang
- 【GOLANG】Go语言学习-select用法
- go语言学习-golang保留字
- [GoLang学习总结]Go语言中的Slice
- 【golang】Go语言学习-select用法