Go 语言通过 cgo
提供了与 C 代码交互的能力,使得开发者能够在 Go 程序中直接调用 C 语言的函数和库。无论是嵌入 C 代码,还是链接 C 动态库,cgo
都能让 Go 程序与 C 语言代码紧密结合,发挥 C 的高性能和 Go 的便利性。
在本篇博客中,我们将逐步展示如何通过 cgo
在 Go 中调用 C 代码,包括:
- 在 Go 中嵌入 C 代码。
- 调用外部 C 动态库(
.so
文件)。
1. 使用 cgo
调用 C 代码
cgo
是 Go 的一个工具,它允许在 Go 程序中嵌入 C 代码或者调用 C 库。cgo
通过特殊的注释语法将 C 代码插入到 Go 代码中,从而实现 C 与 Go 的互操作性。
- 嵌入 C 代码:你可以将 C 代码直接嵌入到 Go 文件中,Go 编译器会在编译过程中处理这些 C 代码。
- 调用 C 库:你可以通过
#cgo
指令链接外部的 C 动态库(如.so
文件),并在 Go 中调用其中的函数。
2. 在 Go 中嵌入 C 代码
2.1 示例:简单的 C 函数调用
假设我们有一个简单的 C 函数,它用于计算两个整数的和。我们将通过 cgo
把 C 代码嵌入到 Go 中,并调用该函数。
libmath.c(C 代码):
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
我们将把这个 C 函数集成到 Go 程序中:
main.go(Go 代码):
package main
/*
#include "libmath.c"
*/
import "C"
import "fmt"
func main() {
// 调用 C 中的 add 函数
result := C.add(2, 3)
fmt.Println("Result from C add function:", result)
}
在这里:
#include "libmath.c"
:将 C 代码嵌入到 Go 文件中。C.add(2, 3)
:通过C
包调用 C 中的add
函数。
2.2 编译和运行
通过 go run
命令运行该 Go 程序,Go 会自动编译并链接嵌入的 C 代码:
$ go run main.go
# 输出结果应为:
Result from C add function: 5
3. 在 Go 中调用 C 动态库
除了将 C 代码嵌入到 Go 中外,cgo
还可以用于调用外部的 C 动态库(.so
文件)。假设我们已经有一个 C 动态库,并希望在 Go 程序中调用其中的函数。
3.1 创建 C 动态库
我们首先创建一个简单的 C 动态库 libmath.so
,该库包含一个 add
函数:
libmath.c(C 代码):
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
编译为动态库:
$ gcc -shared -o libmath.so -fPIC libmath.c
这将生成 libmath.so
文件,这个库将被 Go 程序加载。
3.2 使用 cgo
调用 C 动态库
在 Go 程序中,我们使用 #cgo
指令来告诉 Go 程序链接 libmath.so
动态库,并调用其中的 add
函数。
main.go(Go 代码):
/*
#cgo LDFLAGS: -L. -lmath
#include <stdio.h>
// 声明 C 函数
extern int add(int a, int b);
*/
import "C"
import "fmt"
func main() {
// 调用 C 动态库中的 add 函数
result := C.add(2, 3)
fmt.Println("Result from C add function:", result)
}
在这个 Go 程序中:
#cgo LDFLAGS: -L. -lmath
:告诉 Go 链接libmath.so
动态库。extern int add(int a, int b);
:声明 C 函数add
。
3.3 编译和运行
与之前一样,通过 go run
命令运行该 Go 程序,Go 会自动编译并链接嵌入的 C 代码:
$ go run main.go
# 输出结果应为:
Result from C add function: 5
4. 可能出现的问题
在执行编译过程中,你遇到的错误 cannot open shared object file: No such file or directory
,通常表示在运行 Go 程序时,操作系统无法找到你要链接的 C 动态库(如 libadd.so
)。这通常是因为动态库文件的位置没有正确设置,或者没有正确配置环境变量以便操作系统能够找到它。
为了让程序正确加载 C 动态库,以下是几种解决方法:
4.1 设置 LD_LIBRARY_PATH
环境变量
LD_LIBRARY_PATH
环境变量告诉操作系统在运行时从哪些目录查找共享库文件。如果你的 libadd.so
库不在标准路径(如 /usr/lib
或 /lib
),你需要显式地设置该路径。
4.1.1 在终端设置 LD_LIBRARY_PATH
假设你的 libadd.so
库在当前目录,可以通过以下命令临时设置 LD_LIBRARY_PATH
,使得操作系统能够找到它:
$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
此命令将当前目录(.
)添加到库查找路径中。然后你可以再次运行 Go 程序:
$ go run main.go
4.1.2 持久化 LD_LIBRARY_PATH
设置
如果你希望每次都能自动设置 LD_LIBRARY_PATH
,可以将该命令添加到你的 ~/.bashrc
或 ~/.bash_profile
文件中(假设你使用的是 bash shell):
$ echo 'export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH' >> ~/.bashrc
$ source ~/.bashrc
这样,每次启动终端时,系统就会自动将当前目录添加到库查找路径中。
4.2 使用 -rpath
选项
如果你使用 go build
编译程序,你可以使用 -rpath
选项来告诉链接器共享库的查找路径。这是另一种确保共享库能够在运行时被找到的方式。
4.2.1 编译 Go 程序时指定 rpath
你可以在 Go 编译时使用 -ldflags
参数来设置 rpath
,指定共享库的路径:
$ go build -o main -ldflags "-rpath=."
这将告诉链接器在当前目录(.
)查找共享库。
4.3 将库文件放到标准路径
你也可以将 libadd.so
库文件移动到系统的标准库路径之一,如 /usr/local/lib
或 /lib
,然后执行以下命令刷新链接器缓存:
$ sudo ldconfig
这样,操作系统就能够自动找到 libadd.so
,并且你不需要设置 LD_LIBRARY_PATH
。
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意
腾讯云开发者社区:孟斯特
—