当前位置:   article > 正文

Golang cgo:如何在Go代码中调用C语言代码?

golang cgo

如何在Go代码中调用C语言代码?

Go语言是通过自带的一个叫CGO的工具来支持C语言函数调用,同时我们可以用Go语言导出C动态库接口给其它语言使用。

方式一、直接在 Go 代码中写入 C 代码

检查是否开启cgo工具

首先,要查看是否已经开启cgo工具:go env | findstr CGO。若是0则为关闭状态,需要手动开启:set CGO_ENABLED=1
在这里插入图片描述

编写main.go文件

随后,新建一个 main.go 文件,在注释中写入 C 语言的代码,并且需要导入 Go 提供的 C 包 import "C"。Go 语言在看到导入这个包之后就知道如何去识别和处理注释中的 C 语言代码了,具体示例如下:

注意 C 代码和 i m p o r t " c " ​​语句之间不能有空行 ! \color{red}{注意C 代码和import "c"​​ 语句之间不能有空行 !} 注意C代码和import"c"​​语句之间不能有空行!

package main

// #include <stdio.h>
// #include <stdlib.h>
/*
	void print_fun(char *s) {
		printf("print used by C language: %s\n", s);
	}
*/
import "C"

func main() {
	cStr := C.CString("hello world !") // golang 操作c 标准库中的CString函数
	C.print_fun(cStr)                  // 调用C函数:print_fun 打印输出
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述
这种方式的 C 代码和 Go 代码在同一个文件中,所以大家能明显发现这种方式的代码耦合度太高,仅适用于项目简单单一的情形。

一个更合理的方式应该是 C 代码单独在一个文件。


方式二、Go 直接调用 C 文件

那么,如果已经写好一个封装好的 C 文件代码,Go 语言该如何调用呢?

此时我们需要直接写好 C 相关代码,因为 Go 代码是无法对 C 代码文件进行重写或者修改的。

以如下文件目录结构,创建相关 .h.c.go文件:
在这里插入图片描述

C语言 .h 头文件
  • 编写 ​​hello.h​​ 头文件,示例代码如下:
#ifndef HELLO_H
#define HELLO_H

void print_fun1(char *s);

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
C语言 .c 源文件

编写 ​​hello.c​​ 源文件,示例代码如下:

#include "hello.h"
#include <stdio.h>

void print_fun1(char *s) {
    printf("print used by C language: %s\n", s);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
Go语言 .go 文件

最后编写 ​​main.go​​ 文件:

  • 我们需要在 CFLAGS 参数中填入我们的 GOPath 路径, ​​#cgo CFLAGS: -I xxx​​,xxx这里也可以填写 绝对路径/相对路径
  • 然后在 LDFLAGS 中填入我们的 C 编译后的本地链接文件: ​​#cgo LDFLAGS: xxx/hello.a​​,xxx这里也可以填写 绝对路径/相对路径

注意 C 代码和 i m p o r t " c " ​​语句之间不能有空行 ! \color{red}{注意C 代码和import "c"​​ 语句之间不能有空行 !} 注意C代码和import"c"​​语句之间不能有空行!

具体示例如下:

package main

/*
#cgo CFLAGS: -I .
#cgo LDFLAGS: ./hello.a
#include <stdlib.h>
#include <hello.h>
*/
import "C"

import (
	"unsafe"
)

func main() {
	cStr := C.CString("hello world !") // golang 操作c 标准库中的CString函数
	C.print_fun1(cStr)                 // 调用C函数:print_fun 打印输出

	defer C.free(unsafe.Pointer(cStr)) // 因为 CSstring 这个函数没有对变量申请的空间进行内存释放
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

在这里插入图片描述

编写完以上文件后,距离可运行的程序只有一步之遥了。

编译 c 代码
$ gcc -c *.c   		# 生成.o文件

$ ar rs hello.a *.o # 生成.a文件
ar: creating archive hello.a

$ rm ./hello.o 		# 删除已不需要的.o文件,linux下为rm命令,windows下为del命令
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

生成.a文件需要用到 ar 命令建立或修改备存文件,可参考:Linux ar命令

执行 go 代码
go run main.go
  • 1

三、总结

虽然有些场景下的跨语言调用,会使用到 CGO。但是cgo使用也有其弊端:

  • 代码构建时间变长,且会带来一定的性能损耗
  • 需要特别注意的是:内存泄露。因为 Go 是带垃圾回收机制的编程语言,而使用了 C 后,需要手动的管理内存。例如,上面示例中使用了 cStr := C.CString("hello world !"),最后记得调用C.free(unsafe.Pointer(cStr))释放当前变量申请的内存空间。

更多可参考:golang 操作c 标准库中的CString函数注意事项

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/173739
推荐阅读
相关标签
  

闽ICP备14008679号