简单的 CGO 示例编译失败并出现 "duplicate symbol" 错误

Simple CGO example fails compilation with "duplicate symbol" error

我想用 CGO 做一个从 Go 调用 C 代码的简单例子。但由于某种原因,我无法达到预期。编译失败并出现以下错误:

 go build main.go 
# awesomeProject1/print
duplicate symbol '_do_print' in:
    $WORK/b002/_x002.o
    $WORK/b002/_x003.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

代码:

// print/print.c
#include <stdio.h>

void do_print(char * x){
printf("%s", x);
}
// print/print.go
package print

// #include <print.c>
import "C"

func DoPrint() {
    C.do_print(C.CString("Hello!"))
}

// main.go
package main

import "awesomeProject1/print"

func main() {
    print.DoPrint()
}

如果我将 do_print 函数设为静态,它会编译,但我无法为稍后要集成的第 3 方代码执行此操作。 我是否遗漏了文档中的一些重要部分?教程都是相似的,并声称在我的示例失败的地方工作。请帮忙! 转到版本 1.16.4

这里发生了两件事:

  1. go build 编译 *.c 除了 *.go1
  2. #include <print.c> 完全等同于用 print.c
  3. 的内容覆盖 include 语句

因此,您正在编译print.c的内容两次:一次是CC编译print.c,一次是CGo编译print.go。因此,print.cprint.go 的目标文件都包含在 print.c 中定义的所有导出符号。所以你得到了两份 do_print。将 do_print 设为静态是可行的,因为声明为 static 的函数不会被 CC 导出。

包含 .c 文件(例如 #include <file.c>)几乎总是一个坏主意。 如果您有正当理由 #include 函数体,约定是使用头文件(.h)。这就是 C++ 模板库所做的(比如 Boost、IIRC)。因为C文件通常包含,而H文件通常包含,包含C文件颠覆了预期,因此很可能导致混乱和问题.


1 IIRC,如果包中的任何 Go 文件导入 "C",Go 将编译包中的 C 文件。也就是说,如果一个包中没有任何 Go 文件使用 CGo,Go 将忽略该包中的 C 文件。