写点什么

如何在 Go 代码中运行 C 语言代码

作者:宇宙之一粟
  • 2022 年 3 月 17 日
  • 本文字数:1711 字

    阅读完需:约 6 分钟

如何在 Go 代码中运行 C 语言代码

前言

在前面多篇 Go 系列文章中,我们了解到,Go 语言脱胎于 C 语言,这就意味着在某些更底层的细节中,我们可以使用 C 语言实现,然后通过 Go 来调用相关的 C 代码。其实这一特点,在 Java 的 JVM、Python 的解释器也是通过底层是直接调用 C 实现的。


而本篇文章就来学习一下,如何在 Go 语言中运行 C 程序。


直接在 Go 代码中写入 C 程序

Go 语言通过 cgo 攻击来识别代码中的 C 语言,我们可以通过命令 go env 来查看是否 cgo 工具是否开启。

CGO_ENABLED=1 表示 cgo 工具可用,当设置为 0 时,表示工具不可用。


然后我可以新建一个 CinGo.go 的程序,然后在注释中写入 c 语言的代码。然后导入 Go 提供的 c 包 import "C" ,Go 语言在看到导入这个包之后就知道如何去处理注释中的内容了。

这里我们在 C 代码中写入要给 callC() 函数,然后在 Go 语言中进行调用:

package main
// #include <stdio.h>// void callC() {// printf("Hello World from C!\n");// }import "C"import "fmt"
func main() {
fmt.Println("让我们学习 Go 语句调用 C 程序") C.callC() fmt.Println("调用 C 程序结束")}
复制代码

执行结果:

$ go run CinGo.go让我们学习 Go 语句调用 C 程序Hello World from C!调用 C 程序结束
复制代码

但是,这种方式的 C 代码和 Go 语言代码在同一个文件中,所以大家能明显发现这种方式的代码耦合度太高,仅仅适用于项目简单单一的情形。

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


Go 直接调用 C 文件

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

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

写好 C 头文件

我们在本地 Go 项目中,创建一个 ​​hello.h​​ 的头文件,代码如下:

#ifndef HELLO_H#define HELLO_H
int sayHello(const char *name, char *out);void printMessage(char *message);void cHello();int add(int a, int b);
#endif
复制代码

编写 C 文件

然后编写 ​​hello.c​​ 文件,如下:

#include "hello.h"#include <stdio.h>
int sayHello(const char *name, char *out) { int n;
n = sprintf(out, "Hello, My name is %s!", name);
return n;}
void cHello() { printf("Hello from C!\n");}
void printMessage(char* message) { printf("从 Go 语言接收的信息: %s\n", message);}
int add(int a, int b) { return a + b;}
复制代码

写好 Go 代码

最后编写我们的 ​​main.go​​ 语言:

  • 我们需要在 CFLAGS 参数中填入我们的 GOPath 路径, ​​#cgo CFLAGS: -I /Users/yuzhou_1su/go/src/CinGo​​ 。

  • 然后在 LSFLAGS 中填入我们的 C 编译后的本地链接文件: ​​#cgo LDFLAGS: /Users/yuzhou_1su/go/src/CinGo/hello.a​

package main
// #cgo CFLAGS: -I /Users/yuzhou_1su/go/src/CinGo// #cgo LDFLAGS: /Users/yuzhou_1su/go/src/CinGo/hello.a// #include <stdlib.h>// #include <hello.h>import "C"
import ( "fmt" "unsafe")
func main() {
C.cHello()
a := C.int(1024) b := C.int(2022) result := C.add(a, b) fmt.Println("Reuslt is:", result)
goMessage := C.CString("This is Go") defer C.free(unsafe.Pointer(goMessage)) C.printMessage(goMessage)
}
复制代码

最后代码结构如下:



然后我们首先编译 c 代码:

$ gcc -c *.c      
$ /usr/bin/ar rs hello.a *.oar: creating archive hello.a$ rm hello.o
复制代码

然后再执行 Go 代码,结果如下:

$ go run main.go         Hello from C!Reuslt is: 3046从 Go 语言接收的信息: This is Go
复制代码

总结

在编写上述的小案例过程你中的,都出现了了很多小问题,比如 C 代码和 ​​import "c"​​  语句之间不能有空格。经常会出现找不到 C 函数等等问题。

​总得来说,日常 Go 开发还是不需要此类高级用法,也就是说其实我们平常编程过程中不太需要 cgo,大多数情况下还是尽量用 Go 语言自己实现。如果确实需要使用 C 语言,还是得多去了解 ​​cgo​​ 的文档,以防出错。


灵感来源:

发布于: 刚刚阅读数: 2
用户头像

宇宙古今无有穷期,一生不过须臾,当思奋争 2020.05.07 加入

🏆InfoQ写作平台-第二季签约作者 🏆 混迹于江湖,江湖却没有我的影子 热爱技术,专注于后端全栈,轻易不换岗 拒绝内卷,工作于软件工程师,弹性不加班 热衷分享,执着于阅读写作,佛系不水文

评论

发布
暂无评论
如何在 Go 代码中运行 C 语言代码_C语言_宇宙之一粟_InfoQ写作平台