Go语言学习之cgo(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中用带结尾’ ’的字符数组来表示字符串;而在Go中,string类型是原生类型,因此在两种语言互操作是势必要做字符串类型的转换。
通过C.CString函数,我们可以将Go的string类型转换为C的”字符串”类型,再传给C函数使用。就如我们在本文开篇例子中使用的那样:
s := “Hello Cgon” 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 = %vn", uint32(i))
}
func DoFoo() {
C.dofoo()
}
func main() {
DoFoo()
}
很遗憾,出现了错误: C:UserswangsAppDataLocalTempgo-build603641814cgo_objmain.cgo2.o: In function dofoo': D:/go_workspace/src/cgo/main.go:6: multiple definition ofdofoo’ C:UserswangsAppDataLocalTempgo-build603641814cgo_obj_cgo_export.o
:/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 = %vn", uint32(i))
}
- 暴利驱动的疯狂游戏“外挂”:非法获利可达数百万;X-Agent 后门大升级,俄罗斯 APT28 间谍活动更为隐蔽
- Nginx+keepalived双机热备(主从模式)
- 即使不做程序员,也要学会像程序员一样去思考
- 这5项高科技 正在颠覆未来医疗
- xml的解析
- “微信身份证”来了!下月起全国推广!
- silverlight3的"伪"3D续--图片横向轮换
- 从MapX到MapXtreme2004[5]-自定义工具
- 米市的热潮来袭?三枚域名超六位数美金交易
- 微信新出了逆天功能,或许你用得上
- 网站系统架构梳理-解决高负载高并发
- silverlight 相册雏型
- Mysql优化系列(1)--Innodb引擎下mysql自身配置优化
- 一台服务器多实例mysql做主从复制
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 前端模块化开发--ES6相关知识
- (一)ROS开发平台环境搭建与测试
- 前端模块化开发--Node基础&&WebPack模块化开发
- CNN神经网络--手写数字识别
- 关于模型预测结果好坏的几个评价指标
- SpringBoot微服务架构项目--Union社交平台
- 如何衡量前端基建的效能价值?
- mbedtls | 移植mbedtls库到STM32裸机的两种方法
- 实用小技巧 | 用socket玩转http接口
- 深度学习之人脸识别模型--FaceNet
- Java常用的设计模式
- HW防守|应急溯源分析手册汇总篇
- Python进阶(一)
- Python版本的OpenCV安装
- CVE-2020-16875:Microsoft Exchange RCE复现