
本文详解 go 与 c 互操作中“导出 go 函数供 c 调用”的标准实践,重点解决因 c 端重复声明 `extern int dummy()` 导致的编译错误,并提供可直接运行的结构化示例。
在 Go 中使用 //export 指令可将 Go 函数暴露为 C 可调用符号,但必须严格遵循 cgo 的约定:Go 导出的函数签名(返回类型 + 参数)会自动生成 C 声明,并写入 _cgo_export.c;因此,C 侧不得手动声明同名函数(如 extern int dummy();),否则将触发“conflicting types”编译错误——这正是原始代码报错的根本原因。
正确做法是:仅在 C 头文件(.h)中声明 调用者 函数(如 testc),而将 调用 Go 函数 的逻辑实现在 .c 文件中,且不声明 dummy。Go 的 cgo 工具链会自动处理 dummy 的 C 签名(对应 int dummy(void)),无需、也不应由开发者干预。
以下是推荐的项目结构与完整代码:
dummy.h(仅声明 C 接口):
#ifndef DUMMY_H #define DUMMY_H void testc(); #endif
dummy.c(实现调用逻辑,不声明 dummy):
#include// 注意:此处不加 extern int dummy();! void testc() { dummy(); // 直接调用,链接时由 cgo 提供符号 }
main.go(主程序):
package main // #cgo CFLAGS: -Wno-error=implicit-function-declaration // #include// #include "dummy.h" import "C" import "fmt" //export dummy func dummy() int { fmt.Println("hi you") return 0 } func main() { C.testc() }
✅ 编译运行:
go run main.go # 输出:hi you
⚠️ 关键注意事项:
- //export 函数必须位于 package main 且不能有首字母小写的参数名(C 不支持);
- 所有 //export 函数的参数和返回值类型必须是 C 兼容类型(如 int, *C.char, unsafe.Pointer 等),fmt.Println 等纯 Go 逻辑可安全使用;
- C 文件中禁止对 Go 导出函数做任何 extern 声明——cgo 会自动生成并确保一致性;
- 若需传递复杂数据,应通过 C.CString / C.GoString 或 unsafe.Pointer 转换,避免内存越界。
遵循此模式,即可稳定实现 Go → C → Go 的跨语言调用链,兼顾安全性与可维护性。










