在需要把 Go 能力嵌进现有 C 或 C++ 工程时,`//export` 往往是最直接的一种做法。它能把 Go 函数导出成 C 可调用接口,但真正落地时,读者更关心的通常不是“能不能用”,而是“要怎么写、会生成什么、哪些地方最容易踩坑”。本文就按这条路径展开:先讲清规则,再给出最小示例,最后补上调用方式和实际使用时的判断点。
//export 是什么,解决了什么问题
//export 是 Go 里的特殊注释指令,用来把 Go 函数导出为 C 语言函数。加上这条指令后,Go 编译器会在构建共享库时一并生成对应的 C 头文件声明,C 代码就能按普通函数接口去调用它。
这个能力的核心价值在于跨语言复用:你可以把逻辑写在 Go 里,再交给已有的 C 程序、C++ 模块或其他依赖 C ABI 的系统去接入,而不必把同一套逻辑重复实现一遍。
使用前先看清的语法规则
要让 //export 生效,有两个基本前提:
- 必须在函数声明前写上
//export 函数名 - 文件中必须导入 C 包,也就是
import "C"
这里有一个容易忽略的细节:// 后面要直接跟 export,写法必须是 //export Add 这种形式。导出名要和后面的 Go 函数名对应,位置也必须紧贴函数声明。
另外,示例通常会把代码放在 package main 中,并保留一个空的 main 函数。这不是为了业务逻辑,而是为了满足共享库构建时的组织方式。
最小可运行示例:把 Go 加法函数导出为共享库
如果你想先确认这套机制是否跑通,最简单的方法就是从一个整数加法函数开始。下面这段代码已经具备导出条件:

// add.go
package main
import "C"
//export Add
func Add(a, b C.int) C.int {
return a + b
}
func main() {
// 此处留空,但必须有main函数
}
这段代码里有几个关键信号:
- 使用了
import "C" - 导出函数前写了
//export Add - 参数和返回值使用的是
C.int,便于和 C 接口对应 - 保留了
main函数,即使函数体为空
保存为 add.go 后,使用下面的命令编译:
go build -buildmode=c-shared -o add.so add.go
这里的重点是 -buildmode=c-shared。它告诉 Go 把当前程序构建为可供 C 调用的动态库。命令执行完成后,会得到两个文件:
add.so:生成的动态库add.h:自动生成的头文件
对接 C 代码时,这两个产物缺一不可:一个负责运行时链接,一个负责声明函数接口。
C 侧如何调用:头文件、编译与运行结果
Go 侧导出完成后,C 代码的接入方式其实比较直接。核心就是包含自动生成的头文件,然后按 C 函数正常调用:

// main.c
#include <stdio.h>
#include "add.h" // 包含自动生成的头文件
int main() {
int result = Add(5, 3);
printf("5 + 3 = %dn", result);
return 0;
}
这段示例说明了两件事:一是 Add 已经作为 C 可见符号暴露出来;二是调用方式对 C 程序员来说没有额外心智负担,包含头文件后就能直接使用。
接着编译并运行 C 程序:
gcc -o main main.c add.so
./main
运行结果为:5 + 3 = 8
从这个最小闭环可以看出,//export 的实际工作流就是三步:Go 写函数、构建共享库、C 侧链接调用。只要这条链路跑通,后续再扩展到更复杂的逻辑会容易很多。
实际使用时要注意什么,适合放在哪些场景
内存管理不能想当然
Go 和 C 的内存管理模型并不相同。像整数这样的简单类型通常比较顺手,但一旦涉及字符串或其他复杂数据结构,就需要额外处理分配、传递和释放过程,否则很容易出现泄漏或生命周期不一致的问题。

类型转换要按 C 接口来设计
示例里使用 C.int,就是为了明确 Go 与 C 之间的类型对应关系。虽然 Go 的某些基础类型和 C 看起来接近,但真正导出接口时,仍然应该优先使用 C 包下的类型来表达边界,避免两边理解不一致。
共享库是平台相关产物
通过 go build -buildmode=c-shared 生成出来的动态库并不是“一次编译,到处可用”。不同操作系统、不同目标环境通常都需要重新编译,因此它更适合作为面向特定部署平台的桥接层,而不是完全脱离平台差异的通用二进制。
适合哪些项目场景
//export 比较常见的应用方向主要有三类:
- 性能相关逻辑封装:把某些计算任务放到 Go 中实现,再由既有 C/C++ 程序调用
- 代码复用:直接利用 Go 现成能力或标准库,减少 C 侧重复开发
- 跨语言桥接:把 Go 作为中间实现层,连接其他依赖 C 接口的系统
如果你的目标只是让现有 C 项目快速接入一段 Go 能力,这条路径很合适;但如果接口复杂、数据结构很多,就要提前评估类型设计和内存边界,避免后期维护成本上升。
怎么判断自己该不该用 //export
//export 的价值在于,它把 Go 到 C 的调用路径压缩成了一套比较清晰的工程流程:声明导出函数、按共享库方式编译、通过生成的头文件在 C 中接入。对简单函数来说,上手门槛并不高。
真正需要谨慎的是接口边界。一旦函数参数从整数扩展到字符串、结构体或更复杂的数据对象,内存管理、类型转换和平台构建都会成为必须正视的问题。换句话说,//export 很适合做跨语言入口,但是否适合长期扩展,取决于你的接口复杂度和部署环境。













