【问题标题】:Complicated when compiling in c++ but not in c (gcc)在 c++ 中编译但在 c (gcc) 中编译时复杂
【发布时间】:2012-08-06 03:38:47
【问题描述】:

我在 c++ 中的乘法声明有问题,但在 c 中没有。 您可以查看代码以获取更多信息。

文件 main.c

#ifndef VAR
#define VAR
int var;
#endif
int main(){}

文件 other.c

#ifndef VAR
#define VAR
int var;
#endif

用 gcc 编译

gcc main.c other.c
>> success

用 g++ 编译

g++ main.c other.c
Output:
/tmp/ccbd0ACf.o:(.bss+0x0): multiple definition of `var'
/tmp/cc8dweC0.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status

我的 gcc 和 g++ 版本:

gcc --version
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

g++ --version
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

【问题讨论】:

  • 这很奇怪,虽然你过于复杂了:ideone.com/zdZUg vs ideone.com/swhxi
  • CFLAGS 和 CPPFLAGS 环境变量中有什么内容?
  • 我尝试了 extern "C" 块,但它直到不起作用:(
  • 我已将 other.c 更改为 other.hg++ 编译正常。
  • @VictorHugo 这是作弊。 g++ 会将 .h 文件视为要预编译的头文件。由于 main.c 没有包含它,所以只有一个定义。

标签: c++ c gcc compiler-errors g++


【解决方案1】:

编译模块中全局变量的可见性在 C 和 C++ 之间略有不同。

如果这些是不同的变量,请将它们包含在每个文件的匿名命名空间中。

namespace {
 int var;
}

如果它们打算成为 SAME 变量,则其中一个需要 extern decl-specifier,以避免多个定义。

extern int var;

您的#define VAR 在您发布的示例中没有做任何事情。该定义不跨编译模块进行。

【讨论】:

  • 这里不解释C和C++的区别
【解决方案2】:

这两个#define 指令彼此无关,因为它们位于不同的翻译单元(即源文件)中。编译器完全隔离处理这两个源文件,因此defined(VAR) 始终为假,并且始终包含#ifndef 的内容。

如果您的意思是在多个源文件之间共享一个变量,有一种简单的方法来解决它:在一个源文件中定义它,然后声明它在另一个:

// other.cpp
int var;        // Definition.

// main.cpp
extern int var; // Declaration.

链接时,这些将引用相同的var。更好的是,在标头中声明变量:

// other.h
extern int var;

那么需要var的文件可以简单的包含header:

// main.cpp
#include "other.h"

您观察到的 C 和 C++ 之间的差异与 C 与 C++ 中全局声明标识符的处理方式有关。在 C 中,任意数量的 暂定定义(没有存储类说明符和初始化器)可以由链接器合并为一个符号——只要该符号的所有实际定义最终具有相同链接和存储类。这是使用 weak 链接器符号完成的。

然而,C++ 没有暂定定义的概念,并将没有存储类说明符的外部声明视为定义。因此 G++ 会生成 strong 链接器符号,从而导致链接时发生冲突。

【讨论】:

  • 这里不解释C和C++的区别
  • “编译器进程”:你的意思是c++会在链接前展开所有#ifndef(#ifdef)宏吗?
  • @HaidangKoltec 是的。预编译器和编译器在创建 obect(.o 或 .obj)文件的每个 .cpp 文件上运行。然后链接器将所有 .obj 文件组合成一个可执行文件。
【解决方案3】:

包含保护是翻译单元本地的。这意味着当您在一个.cpp 文件中执行#define VAR 时,它不会在任何其他文件中定义。

【讨论】:

    【解决方案4】:

    由于变量 var 的多个定义,您的代码在 C 和 C++ 中的形式都不正确。只是这种类型的错误传统上被 C 编译器忽略为流行的非标准扩展。 C语言规范中甚至提到了这个扩展

    J.5 常用扩展

    以下扩展在许多系统中广泛使用,但并非所有人都可以移植 实施。 [...]

    J.5.11 多个外部定义

    一个对象的标识符可能有多个外部定义,带有或 没有明确使用关键字 extern;如果定义不一致,或超过 一个已初始化,行为未定义(6.9.2)。

    但从形式上讲,在 C 和 C++ 语言中,您都会遇到完全相同的多重定义错误。让你的 C 编译器表现得更加迂腐(禁用扩展,如果它有一个选项),你的 C 编译器也会产生与你的 C++ 编译器完全相同的错误。

    同样,您的代码包含变量 var 的多个定义,这在 C 和 C++ 中都是错误的。您的 #ifdef 指令根本无法解决任何问题。预处理指令在这里无法为您提供帮助。预处理器在每个翻译单元中本地独立地工作。它无法跨翻译单元查看。

    如果你想创建一个全局变量(即所有翻译单元共享的同一个变量),你需要只定义一个变量

    int var;
    

    在一个且只有一个翻译单元中。所有其他翻译单元应收到var的非定义声明

    extern int var;
    

    后者通常放在头文件中。

    如果您在每个翻译单元中需要一个单独的独立变量var,只需在每个翻译单元中将其定义为

    static int var;
    

    (尽管在 C++ 中 static 的这种用法现在已被无名命名空间弃用并取代)。

    【讨论】:

    • +1。如果您真的想确保它已定义,请定义(再次仅在一个翻译单元中)'int var = 0;'。然后编译器必须将其视为定义。
    猜你喜欢
    • 2021-05-24
    • 2015-05-08
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 2017-07-07
    • 1970-01-01
    • 2013-05-03
    • 1970-01-01
    相关资源
    最近更新 更多