6

我在 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.
4

4 回答 4

12

由于 variable 的多个定义,您的代码在 C 和 C++ 中的形式都不正确var。只是这种类型的错误传统上被 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现在已被无名命名空间弃用并取代)。

于 2012-08-08T23:15:06.860 回答
0

这两个#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++ 会生成链接器符号,从而导致链接时发生冲突。

于 2012-08-08T23:11:31.440 回答
-1

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

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

namespace {
 int var;
}

如果它们打算成为 SAME 变量,则其中之一需要一个externdecl 说明符,以避免多个定义。

extern int var;

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

于 2012-08-08T23:10:43.670 回答
-1

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

于 2012-08-08T23:12:51.143 回答