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 <stdio.h>
// #include <stdlib.h>
// #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 <stdlib.h>
// #include <stdio.h>
// #include <errno.h>
// 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)
    }
}

运行这个例子:

$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的静态链接库

posted @   guanyubo  阅读(183)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示