cgo--在Go中链接外部C库

简介: cgo--在Go中链接外部C库

虽然我们可以在Go源码文件中直接定义C类型、变量和C函数,但从代码结构上来讲,在Go源文件中大量编写C代码并不是Go推荐的惯用法。那么如何将C函数和变量定义从Go源码中分离出去单独定义呢?我们很容易想到将C的代码以共享库的形式提供给Go源码。

Go提供了#cgo指示符,可以用它指定Go源码在编译后与哪些共享库进行链接。我们来看一下例子:

// go-cgo/foo.go
package main

// #cgo CFLAGS: -I${SRCDIR}
// #cgo LDFLAGS: -L${SRCDIR} -lfoo
// #include
// #include
// #include "foo.h"
import "C"
import "fmt"

func main() {
fmt.Println(C.count)
C.foo()
}
我们看到在上面的例子中,通过#cgo指示符告诉Go编译器在当前源码目录(${SRCDIR}会在编译过程中自动转换为当前源码所在目录的绝对路径)下查找头文件foo.h,并链接当前源码目录下的libfoo共享库。C.count变量和C.foo函数的定义都在libfoo共享库中。

我们来创建这个共享库:

// chapter9/sources/go-cgo/foo.h

extern int count;
void foo();
// chapter9/sources/go-cgo/foo.c

include "foo.h"

int count = 6;
void foo() {
printf("I am foo!\n");
}
$gcc -c foo.c
$ar rv libfoo.a foo.o
我们用ar工具成功创建了一个静态共享库文件libfoo.a。

接下来构建并运行foo.go:

$go build foo.go
$./foo
6
I am foo!
我们看到foo.go成功链接到libfoo.a并生成最终的二进制文件foo。

Go同样支持链接动态共享库,我们用下面的命令将上面的foo.c编译为一个动态共享库:

$gcc -c foo.c
//$gcc -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o (在linux上)
$gcc -shared -o libfoo.so foo.o
重新编译foo.go,并查看(在Linux上可以使用ldd,在macOS上使用otool)重新生成的二进制文件foo的动态共享库依赖情况:

$> go build foo.go
$otool -L foo
foo:
libfoo.so (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
有一点值得注意的是,Go支持多返回值,而C并不支持,因此当将C函数用在多返回值的Go调用中时,C的errno将作为函数返回值列表中最后那个error返回值返回。下面是个例子:

// chapter9/sources/go-cgo/c_errno.go

package main

// #include
// #include
// #include
// int foo(int i) {
// errno = 0;
// if (i > 5) {
// errno = 8;
// return i - 5;
// } else {
// return i;
// }
//}
import "C"
import "fmt"

func main() {
i, err := C.foo(C.int(8))
if err != nil {
fmt.Println(err)
} else {
fmt.Println(i)
}
}
[kod.chinaweiyuan.com)
[kod.dtjxzx.com)
[kod.1d-cn.com)
[kod.cd-shanye.com)
[kod.hrbshenou.com)
[kod.wogou168.com)
[kod.aoshageya.com)
运行这个例子:

$go run c_errno.go
exec format error
exec format error就是errno为8时的错误描述信息。我们可以在C运行时库的errno.h中找到errno=8与这段描述信息的联系:

define ENOEXEC 8 / Exec format error /

cgo构建引用c的静态链接库

相关文章
|
24天前
|
Shell Go API
Go语言grequests库并发请求的实战案例
Go语言grequests库并发请求的实战案例
|
2月前
|
Rust 安全 算法
Go标准库的新 math/rand
Go标准库的新 math/rand
|
2天前
|
Rust Go C语言
Python通过C动态链接库调用Go语言函数
Python通过C动态链接库调用Go语言函数
|
2月前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
|
2月前
|
存储 JSON 前端开发
一文搞懂 Go 1.21 的日志标准库 - slog
一文搞懂 Go 1.21 的日志标准库 - slog
65 2
|
2月前
|
Prometheus Cloud Native Go
Go 1.22 标准库 slices 新增函数和一些旧函数增加新特性
Go 1.22 标准库 slices 新增函数和一些旧函数增加新特性
|
2月前
|
XML Go 数据库
|
2月前
|
Go
【go笔记】标准库-strconv
【go笔记】标准库-strconv
|
2月前
|
Go 索引
【go笔记】标准库-strings
【go笔记】标准库-strings
|
2月前
|
Go
【go笔记】使用标准库flag解析命令行参数
【go笔记】使用标准库flag解析命令行参数