用 c++ 编译但不在 c (gcc) 中编译时很复杂

Complicated when compiling in c++ but not in c (gcc)

本文关键字:编译 gcc 很复杂 c++      更新时间:2023-10-16

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

文件 main.c

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

文件 其他.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.

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

J.5 常见扩展

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

J.5.11 多个外部定义

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

但从形式上讲,你在 C 语言和C++语言中都有完全相同的多定义错误。要求你的 C 编译器表现得更学究(禁用扩展,如果它有这样的选项),你的 C 编译器也会生成与你的 C++ 编译器相同的错误。

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

如果要创建一个全局变量(即所有翻译单元共享的同一变量),则需要对该变量进行一个且仅一个定义

int var;

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

extern int var;

后者通常放在头文件中。

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

static int var;

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

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

如果你的意思是在多个源文件之间共享一个变量,有一个简单的方法可以做到这一点:在一个源文件中定义它,然后在另一个源文件中声明它:

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

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

// other.h
extern int var;

然后需要var的文件可以简单地包含标头:

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

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

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

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

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

namespace {
 int var;
}

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

extern int var;

您的#define VAR在您发布的示例中没有执行任何操作。该定义不会跨编译模块进行。

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