Go语言学习之cgo(golang与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

:/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))

}

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2017-08-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏cs

c++那些事儿5.0 模板

模板 是泛型编程的重点,在后面的STL中,模板的功能得到广泛运用。 在java中泛型,集合都有模板的身影,弄清模板可以让我们更加深入学习。 ---- 可讲的...

36212
来自专栏编程之旅

Swift中的"警卫队"

大半个月没有更新自己的博客了,最近在忙一个新项目时间非常紧张,所以最近的博客更新进度就要稍微放缓一点了。

861
来自专栏java达人

js的回调函数详解

在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用。既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递...

3595
来自专栏极乐技术社区

使用ES6新特性开发微信小程序(1)

ECMAScript 6(简称ES6)是JavaScript语言的最新标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。 ...

2255
来自专栏SpringBoot

String、StringBuffer与StringBuilder之间区别

最近学习到StringBuffer,心中有好些疑问,搜索了一些关于String,StringBuffer,StringBuilder的东西,现在整理一下。

1022
来自专栏个人分享

Scala第四章学习笔记(面向对象编程)

DelayedInit特质是为编译器提供的标记性的特质。整个构造器被包装成一个函数并传递给delayedInit方法。

881
来自专栏北京马哥教育

只要3分钟,Python生成器原理详解

翻译:你逗比 segmentfault.com/a/1190000011330511 这篇文章是对 500 Lines or Less 一书中高效爬虫一章的部...

3535
来自专栏黑泽君的专栏

【Java面试复习经典】传智播客Java就业班入学测试题及答案解析(2012年版)

  共50道题,每道题2分,总分100分,80分为合格。   注意,题目有多选,也有单选。请认真作答。

1833
来自专栏一个会写诗的程序员的博客

Kotlin 中 有趣 好玩的高阶函数《Kotlin极简教程》正式上架:

922
来自专栏一个会写诗的程序员的博客

Kotlin 简单优雅的高阶函数Kotlin 简单优雅的高阶函数《Kotlin极简教程》正式上架:1 . Kotlin 函数式编程: 函数的组合。一切皆是函数。2. 简单好用的 Kotlin 类型别名

函数代表一种关系 f 的蕴涵逻辑流。这种蕴涵逻辑流,其实就是映射(Mapping)。

1173

扫码关注云+社区

领取腾讯云代金券