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: 恋水无意
腾讯云开发者社区:孟斯特
—