Go 中 //export 必须紧贴函数定义前一行,中间不能有空行或缩进;函数签名仅支持 C 兼容类型;C 头文件不可声明 Go 导出函数;C 实现文件直接调用导出函数;C 源码须独立编译;动态库需用 -buildmode=c-shared 确保符号可见。

Go 导出函数时 //export 必须紧贴函数定义前一行
Go 中的 //export 不是注释,而是 cgo 解析指令;它必须与后续函数定义之间**不能有空行、不能有其他语句、不能缩进**。否则 cgo 会忽略该导出,导致 C 侧链接时报 undefined symbol。
-
//export myfunc后直接跟func myfunc() {...},中间无空行 - 函数签名必须只含 C 兼容类型:如
int,int32,*C.char,unsafe.Pointer,不能用string、[]byte、结构体等 Go 原生类型 - 返回值若为
int,cgo 自动生成 C 签名int myfunc(void);若返回void,Go 函数需声明为func myfunc()(无返回)
C 头文件里不能声明 Go 导出函数
常见错误是把 extern int dummy(); 写进 dummy.h 或在 .c 文件里手动声明 —— 这会导致编译时报 conflicting types for 'dummy'。因为 cgo 已在 _cgo_export.c 中生成了该符号的唯一声明,重复声明即冲突。
- C 头文件(如
api.h)只声明 C 侧提供的接口,例如void run_from_c(); - C 实现文件(如
bridge.c)中直接调用dummy(),不加extern声明 - Go 文件里只通过
//export dummy暴露,不向 C 提供任何头文件声明
构建时 C 文件不能被 #include 进 Go 的 C 块
如果在 Go 源码的 /* #include "helper.c" */ 中直接内联 C 源文件,cgo 会把它复制进中间生成文件,并和独立编译的 helper.o 一起链接,造成 duplicate symbol 错误。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 所有 C 实现必须放在独立的
.c文件中,由 cgo 自动编译链接 - Go 的 C 块(
/* ... */)里只放#include <stdio.h>或#include "api.h"这类头文件引用 - 确保
.c文件与.go文件在同一目录,或通过#cgo LDFLAGS: -L/path/to/objs显式指定目标文件路径
动态库导出需额外控制符号可见性
当 Go 编译成动态库(如 libgoapi.so)供外部 C 程序加载时,//export 函数默认是隐藏的;不加处理,dlsym 找不到符号。
立即学习“go语言免费学习笔记(深入)”;
- 必须添加
// #cgo LDFLAGS: -shared -fPIC构建共享库 - Linux 下需加
// #cgo CFLAGS: -fvisibility=hidden,再对导出函数加__attribute__((visibility("default")))—— 但更稳妥做法是:用go build -buildmode=c-shared,它自动处理符号导出 - 验证导出符号是否存在:
nm -D libgoapi.so | grep dummy,应看到T dummy(大写 T 表示全局定义)
实际项目中,最容易被忽略的是 C 头文件与 Go 导出之间的职责边界:C 头文件不是给 Go 看的,而是给 C 调用者看的;Go 导出函数的 C 签名完全由 cgo 掌控,人为干预只会破坏一致性。

















