【问题标题】:c & c++ default global variable linkage, multiple declaration & definition problemc&c++默认全局变量链接,多重声明&定义问题
【发布时间】:2011-09-16 07:05:53
【问题描述】:

例如:

code1.c / .cpp

int a;

// ... and so on

code2.c / .cpp

int a;

int main(void) {
    return 0;
}

去编译:

$gcc code1.c code2.c      # this is fine
$

$g++ code1.cpp code2.cpp  # this is dead
/tmp/ccLY66HQ.o:(.bss+0x0): multiple definition of `a'
/tmp/ccnIOmPC.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status

C和C++之间有没有全局变量链接的区别?

【问题讨论】:

标签: c++ c linkage multiple-definition-error one-definition-rule


【解决方案1】:

g++ 编译器比 gcc 编译器更严格。 它还取决于 gcc 的版本,可能是更高版本的 gcc,即 4.X 以后它会给出相同的错误。

使用extern 避免

【讨论】:

  • 两个编译器都同样严格。他们以不同的规则实施不同的标准。说 C++ 比 ANSI C 更严格就像说法语比英语更严格:在某些情况下可能是正确的,但归根结底你是在比较苹果和橘子
【解决方案2】:

解决问题的三种方法:

  1. 如果两个文件中的变量a 相同,则必须在除一个文件之外的所有文件中将其声明为externextern 关键字告诉链接器该名称位于另一个文件中。

  2. 您可以使用static 关键字将变量的范围限制为一个文件。在其中声明它。

  3. 或者你可以使用无名命名空间。

【讨论】:

  • 也有-zmuldefs,但它通常被认为是一种邪恶的解决方案。
【解决方案3】:

这不是严格合法的。 int a; 是 C 语言中的暂定定义。在 C 语言中,每个具有外部链接的对象的每个翻译单元允许多个暂定定义和最多一个非暂定定义,但在程序中的所有翻译单元中只能有一个定义。

这是一种常用的扩展,允许在 C 中跨多个翻译单元进行暂定定义,只要不超过一个翻译单元包含非暂定定义,但它不是严格标准。

在 C++ 中,int a; 只是一个定义 - 没有暂定的概念 - 在程序的翻译单元中对一个对象进行多个定义仍然是非法的。

对于C的情况,你不妨看看this question

【讨论】:

  • 不是扩展;它是标准的一部分,就像 K&R 函数语法一样(即:如果编译器没有实现它,那么它就是不兼容的)。 ANSI 标准要求支持这些东西,以便 C89 之前的代码库仍然可以使用现代编译器进行编译。 C++ 从未提出过这样的要求,因为它从一开始就从未打算与 C 兼容。如果是这样,它将是弱类型,并且该标准将要求编译器也遵守 ANSI C 标准。
  • 标准中提及暂定定义的地方:ISO/IEC 9899:1999 §6.9 外部定义
【解决方案4】:

两者都是非法的,但 C 编译器通常会实现扩展。见this answer

【讨论】:

  • 这在 C 中并不违法。按照标准,它是完全合法的。请参阅 ISO/IEC 9899:1999 §6.9 外部定义,其中 C99 标准描述了暂定定义。不是扩展。如果你的编译器没有实现它,并声称它是一个“不必要的扩展”,那么供应商在不知不觉中违反了标准。
  • @BradenBest code1.ccode2.c 中的定义都是暂定定义,但是当到达每个文件的末尾时,它们都会成为定义。因此,变量a 将在两个不同的翻译单元中定义。这就是“J.5.11 多个外部定义”所指的情况。如果文件code1.ccode2.c 被连接并编译为一个文件就可以了。
  • 有道理,但如果我们纯粹在谈论 C,那么它仍然是合法的,因为链接(它将失败)取决于实现细节。链接器可以多次看到相同的符号并任意决定丢弃其中一个定义。编译器本身并不关心。
  • @BradenBest 这不是我对6.9 paragraph 5 的理解:如果在表达式中使用了使用外部链接声明的标识符(而不是作为 sizeof 或 _Alignof 运算符的操作数的一部分,其result 是一个整数常量),在整个程序的某个地方,标识符应该有一个外部定义;否则,不得超过一个
  • 该规则在技术上是不可执行的,因为您也可以单独编译每个模块,然后将它们的输出一起编译成可执行文件,链接器会阻止您。据我了解,一旦你进入目标文件、程序集输出和可执行/abi 特定的东西,它就不再与 C 相关,因为链接器没有实现 C。字符限制挂起。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多