
本文详解 go 与 c 互操作中“从 c 调用 go 函数”的标准实践,重点解决因重复声明导致的 `conflicting types for 'dummy'` 编译错误,并提供可运行的结构化示例。
在 Go 中通过 cgo 导出函数供 C 代码调用,是实现跨语言集成的重要能力。但初学者常因 C 头文件与 Go 导出机制不匹配而遭遇编译失败——典型错误如 conflicting types for 'dummy',其根源在于:Go 的 //export 机制会自动生成 C 可见的函数声明(位于 _cgo_export.c),若你在 C 源码(如 wrapper.c)中又手动声明了同名函数(如 extern int dummy();),就会造成类型冲突。
正确的做法是:仅在 C 头文件(.h)中声明 C 侧函数接口,C 实现文件(.c)中调用 Go 导出函数,而 Go 文件中只通过 //export 声明,不暴露任何 C 侧声明。这样可确保类型由 Go 单一权威生成,避免重复定义。
以下是推荐的三文件结构(清晰、可维护、符合 cgo 最佳实践):
✅ dummy.h(C 接口头文件,仅声明 C 函数):
#ifndef DUMMY_H #define DUMMY_H void testc(); #endif
✅ dummy.c(C 实现文件,直接调用 Go 导出函数):
#include// 注意:无需声明 dummy —— 它由 Go 自动生成并链接 void testc() { dummy(); // 直接调用,链接器会解析 }
✅ main.go(Go 主文件,导出函数并调用 C):
package main // #cgo CFLAGS: -Wno-error=implicit-function-declaration // #cgo LDFLAGS: -lm // #include// #include "dummy.h" import "C" import "fmt" //export dummy func dummy() int { fmt.Println("hi you") return 0 } func main() { C.testc() // 调用 C 函数,间接触发 Go 函数 }
? 关键注意事项:
- ✅ 不要在 .c 或 .h 中声明 Go 导出函数(如 extern int dummy();):Go 已通过 _cgo_export.c 提供完整 C 签名(int dummy(void)),手动声明极易因签名不一致(如返回值、参数列表)引发冲突;
- ✅ 使用 #include "dummy.h"(而非 "wrapper.c"):头文件仅含接口,避免 C 编译器误将实现文件当作头文件预处理;
- ✅ 若需传递参数或返回复杂类型,请确保 Go 函数签名使用 C 兼容类型(如 C.int, *C.char, unsafe.Pointer),并配合适当的 C.CString/C.GoString 转换;
- ✅ 构建时建议使用 go build 而非 go run(尤其含 C 依赖时),或确保工作目录下无残留 .o/_obj 编译缓存。
执行 go build -o app . && ./app 即可成功输出 hi you。该模式兼顾安全性与可扩展性,是生产环境中 Go/C 混合编程的标准起点。










