【问题标题】:Simple CGO example fails compilation with "duplicate symbol" error简单的 CGO 示例因“重复符号”错误导致编译失败
【发布时间】:2021-09-12 02:32:42
【问题描述】:

我想做一个简单的例子,用 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

【问题讨论】:

  • C 文件和 Go 文件都被编译,所以 print.c 的内容被编译了两次。通常,包含.c 文件是个坏主意。
  • 我明白了!现在可以了。你想创建一个答案还是我应该?
  • 我写了一个答案。我更详细地介绍了,因为我的评论并没有太多的答案。

标签: go cgo


【解决方案1】:

这里发生了两件事:

  1. go build 编译 *.c 和 *.go1
  2. #include &lt;print.c&gt; 完全等同于用 print.c 的内容覆盖 include 语句

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

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


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

【讨论】:

    猜你喜欢
    • 2012-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-27
    • 2022-10-04
    • 2015-12-16
    相关资源
    最近更新 更多