Go和C语言操作

 2024-02-29 04:05:15  阅读 0

Go和C语言操作

Go 有很强的 C 背景。 除了语法的继承之外,它的设计者和它的设计目标都与C语言有着千丝万缕的联系。 在Go和C语言的互操作性方面(),Go提供了强大的支持。 尤其是在Go中使用C时,甚至可以直接在Go源文件中编写C代码,这是其他语言无法比拟的。 以下是小编为大家带来的关于Go和C语言的操作知识。 欢迎阅读。

1.Go调用C代码的原理

这是一个简短的例子:

复制代码

主要的

// #

// #

/*

无效打印(字符* str){

("%s", 字符串);

*/

“C”

””

函数主() {

s := "你好,Cgo"

cs := C.(s)

C.print(cs)

C.free(.(cs))

复制代码

与“正常”Go 代码相比,上面的代码有几个“特殊”的地方:

1)开头注释中出现C file字样

2)C函数print在注释中定义

3)名为C的“包”

4)上述的C函数——print实际上是在main函数中调用的。

是的,这些就是Go源码中调用C代码的步骤。 可见我们可以直接在Go源码文件中编写C代码。

首先,Go源文件中的C代码需要用注释包裹起来,就像上面的头文件和打印函数定义一样;

其次,“C”语句是必须的,并且它与上面的C代码不能用空行隔开,必须紧密相连。 这里的“C”并不是包名,而是一个类似于命名空间的概念,或者可以理解为伪包。 C语言的所有语法元素都在这个伪包下;

最后,在访问C语法元素时,必须在其前面添加伪包前缀,例如上面代码中的C.uint和C.print、C.free等。

我们如何编译这个go源文件呢? 事实上,它与“正常”的Go源文件没有什么不同。 还是可以直接通过go build或者go run来编译执行。 但是在实际的编译过程中,go会调用一个叫做cgo的工具。 cgo会识别并读取Go源文件中的C元素,将其提取出来交给C编译器进行编译,最后与Go源代码编译后的目标文件结合起来。 链接成可执行程序。 这样,我们就不难理解为什么Go源文件中的C代码被注释包裹了。 这些特殊语法可以被Cgo识别和使用。

2. Go中使用的C语言类型

1.原生型

* 数字类型

在 Go 中,您可以通过以下方式访问 C 的原生数值类型:

复制代码

C. 字符,

C.schar(字符),

C.uchar(字符),

C.短,

C.(短),

C.int、C.uint(int)、

C.长,

C.ulong(长),

C.(长长),

C.(长长),

C.浮动,

C。

复制代码

Go 的数值类型与 C 中的 . 类型并不是一一对应的。因此,在使用其他类型的变量时,显式转换操作是必不可少的,例如 Go doc 中的示例:

复制代码

函数 () int {

int(C.())//C.long -> Go 的 int

func 种子(i int) {

C.(C.uint(i))//Go uint -> C uint

复制代码

* 指针类型

根据 Go 语法,原生数值类型的指针类型可以在前面加 *,例如 var p *C.int。 而void*比较特殊,用 表示。 在围棋中。 任何类型的指针值都可以转换为 .type,而 .type 值也可以转换为任何类型的指针值。 .也可以与此类型相互转换。 由于 的指针类型。 不能进行算术运算,转换后可以进行算术运算。

* 字符串类型

C语言中没有正式的字符串类型。 在C语言中,使用尾随''的字符数组来表示字符串; 在Go中,类型是原生类型,因此需要使用字符来在两种语言之间进行互操作。 字符串类型转换。

通过C.函数,我们可以将Go类型转换为C“字符串”类型,然后传递给C函数使用。 就像我们在本文开头的示例中使用的那样:

s := "你好,Cgo"

cs := C.(s)

C.print(cs)

然而这样转换后得到的C字符串cs无法被Go的gc管理。 我们必须手动释放cs占用的内存。 这就是例子中最后调用C.free来释放cs的原因。 C内部分配的内存不知道Go中的GC,所以记得释放它。

C. 可以将C字符串(*C.char)转换为Go类型,例如:

复制代码

// #

// #

// char *foo = "";

“C”

“FMMT”

函数主() {

……

fmt.("%s", C.(C.foo))

复制代码

静态库使用方法_静态库的使用_go使用c静态库

*数组类型

C语言中的数组与Go语言中的数组有很大不同。 后者是值类型,而前者和C中的指针大多数情况下可以随意转换。 目前看来,两者之间还不能直接显式地进行转换,官方文档也没有解释。 但是我们可以通过编写转换函数将C数组转换为Go (由于Go中的数组是值类型并且其大小是静态的,因此转换为Slice更通用)。 以下是整数数组转换的示例。 :

复制代码

// int[] = {1, 2, 3, 4, 5, 6, 7};

func ( ., size int) ( []int) {

p := ()

对于我:=0; 我<大小; 我++ {

j := *(*int)(.(p))

= (, j)

p += .(j)

函数主() {

……

:= (.(&C.[0]), 7)

fmt.()

复制代码

执行结果输出:[1 2 3 4 5 6 7]

这里需要注意的是,Go编译器无法自动将C值转换为数组的地址,因此数组变量不能像C中使用数组一样直接传递给函数,而是传递第一个元素的地址该数组被传递给函数。

2. 定制类型

除了原生类型之外,我们还可以访问 C 中的自定义类型。

* 枚举(枚举)

复制代码

// 枚举颜色 {

// 红色的,

// 蓝色的,

//

// };

var e, f, g C. = C.RED, C.BLUE, C.

fmt.(e、f、g)

复制代码

输出:0 1 2

对于命名的C枚举类型,我们可以通过C来访问该类型。如果是匿名枚举,似乎只能访问它的字段。

* 结构()

复制代码

// {

// 字符 *id;

// int 年龄;

// };

id := C.("1247")

var C. = C.{id, 21}

fmt.(C.(.id))

fmt.(.年龄)

C.free(.(id))

复制代码

输出:

第1247章

21

与enum类似,我们可以通过C来访问C中定义的结构体类型。

* 联盟

这里我尝试使用与访问相同的方法来访问 C 联合:

复制代码

// #

// 联合栏 {

// 字符 c;

// int i;

//d;

// };

“C”

函数主() {

var b *C。 = 新(C.)

BC = 4

fmt.(b)

复制代码

然而编译时go报错:bc(type *[8]byte has no field or c)。 从错误信息来看,Go对待联合的方式与其他类型不同。 似乎将联合视为[N]字节,其中N是联合中最大字段的大小(四舍五入),因此我们可以如下处理C:

函数主() {

var b *C。 = 新(C.)

b[0] = 13

b[1] = 17

fmt.(b)

输出:&[13 17 0 0 0 0 0 0]

*

在Go中访问使用Go定义的别名类型时,访问方法与原始实际类型访问方法相同。 喜欢:

复制代码

// int myint;

var a C.myint = 5

fmt.(a)

// ;

var m C.

复制代码

从示例中可以看出,对于原生类型的别名,可以直接访问新类型名称。 对于复合类型别名,需要按照原复合类型的访问方法来访问新的别名。 例如,如果实际类型是,那么使用时必须添加前缀。

3.在Go中访问C变量和函数

事实上,在上面的例子中,我们已经演示了如何在Go中访问C变量和函数。 一般的方法是加上C前缀,特别是对于C标准库中的函数。 然而,虽然我们可以直接在Go源码文件中定义C变量和C函数,但从代码结构上来说,在Go源码中编写大量的C代码似乎并没有那么“专业”。 那么如何将C函数和变量定义从Go源码中分离出来,单独定义呢? 我们很容易想到以共享库的形式将C代码提供给Go源代码。

Cgo 提供了 #cgo 指令来指定 Go 源代码编译后将链接到哪些共享库。 让我们看一个例子:

复制代码

主要的

// #cgo : -L ./ -lfoo

// #

// #

// #“foo.h”

“C”

“FMMT”

函数主() {

fmt.(C.count)

C.foo()

复制代码

我们看到,在上面的例子中,#cgo指令用于告诉go编译器链接当前目录中的共享库。 C.count 变量和 C.foo 函数的定义都在共享库中。 让我们创建这个共享库:

// foo.h

复制代码

整数计数;

无效 foo();

//foo.c

#“foo.h”

整数计数 = 6;

无效 foo() {

(“我是富!”);

复制代码

$> gcc -c foo.c

$>ar rv .a foo.o

我们首先创建了一个静态共享库.a,但是在编译Go源文件时遇到了问题:

$> 去构建 foo.go

# -线-

/tmp/go-/-line-.a(foo.cgo2.)(.text): foo: 不是

foo(0): 不是

提示foo函数未定义。 通过-x选项打印了具体的编译细节,但没有发现问题。 不过,我在Go问题列表中发现了一个issue(),其中提到当前版本的Go不支持链接静态共享库。

然后让我们尝试创建一个动态共享库:

$> gcc -c foo.c

$> gcc - -Wl,-,.so -o .so foo.o

再次编译foo.go确实会成功。 执行 foo.

$> 去构建 foo.go && 去

我是福!

还有一点值得注意的是,Go 支持多个返回值,但 C 不支持。 因此,当一个C函数用于具有多个返回值的调用时,C的errno将作为err返回值返回。 下面是一个例子:

复制代码

主要的

// #

// #

// #

// int foo(int i) {

// 错误号 = 0;

// 如果 (i > 5) {

// 错误号 = 8;

// 我 - 5;

// } 别的 {

//我;

// }

//}

“C”

“FMMT”

函数主() {

我,错误:= C.foo(C.int(8))

如果错误!= nil {

fmt。(错误)

} 别的 {

fmt.(i)

复制代码

$> 去运行 foo.go

执行错误

errno为8,其含义可以在errno.h中找到:

# 8 /* 执行错误 */

这确实是一个“执行错误”。

4. 在 C 中使用 Go 函数

与在Go中使用C源代码相比,在C中使用Go函数的场合较少。在Go中,可以使用“+函数名称”导出Go函数以在C中使用。看一个简单的例子:

go使用c静态库_静态库的使用_静态库使用方法

复制代码

主要的

/*

#

空白 ();

无效栏(){

(“我是酒吧!”);

();

*/

“C”

“FMMT”

//

函数(){

fmt。(“我是一个!”)

函数主() {

C.bar()

复制代码

但是当我们编译Go文件时,我们得到了以下错误消息:

# -线-

/tmp/go-/-line-/_obj/bar.cgo2.o:在“bar”中:

./bar.go:7: 属于“bar”

/tmp/go-/-line-/_obj/.o:/home//test/go/bar.go:7:首先在这里

: ld 1 退出

代码看似没有问题,但就是无法编译,总是提示“多重定义”。 翻阅Cgo文档,我发现了一些线索,原来是

有一个:如果您使用任何 // ,那么其中的 C 代码可能只是 ( int f();),而不是 (int f() { 1; })。

看来 // int f() 和 // f 不能放在 Go 源文件中。 我们将 bar.go 分成两个文件:bar1.go 和 bar2.go:

// 酒吧1.go

复制代码

主要的

/*

#

空白 ();

无效栏(){

(“我是酒吧!”);

();

*/

“C”

函数主() {

C.bar()

复制代码

// 酒吧2.go

复制代码

主要的

“C”

“FMMT”

//

函数(){

fmt。(“我是一个!”)

复制代码

编译并执行:

$> go build -o bar bar1.go bar2.go

$>酒吧

我是酒吧!

我是一个!

就我个人而言,我觉得Go导出函数在C中使用的能力仍然非常有限。 两种语言的调用约定不同,类型无法一一对应,而且Go中的Gc这样的高级函数很难完美实现导出Go函数的功能。 功能仍然无法完全脱离Go环境,实用性似乎受到了影响。

5、其他

尽管Go提供了与C强大的互操作功能,但它仍然不完善。 比如不支持直接调用Go中参数个数可变的()等(因此文档中经常使用fputs)。

这里的建议是:尽量缩小Go和C之间的互操作范围。

这是什么意思? 如果你在Go中使用C代码,那么尝试在C代码中调用C函数。 Go 最好只使用您封装的 C 函数。 不要看起来像下面的代码:

C.fputs(…)

C.阿托伊(..)

C。(..)

相反,这些C函数调用被封装成一个C函数,而Go只知道这个C函数。

C.foo(..)

相反,在 C 中使用 Go 导出函数也是如此。

【Go与C语言的操作】相关文章:

C语言低级运算10-07

C语言位运算有11-24位

C语言文件操作函数11-04

C语言中什么是位运算?10-06

C语言文件操作中fgets和fputs函数讲解10-22

C语言文件操作函数详解11-20

C语言与-05

使用c语言操作文本的基本方法11-20

C语言文件操作分析及示例代码详解10-03

标签: 代码 复制 操作

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码