77

我在 Win32 控制台应用程序中有 2 个文件,A.cpp 和 B.cpp。

两个文件都只包含以下两行代码:

#include "stdafx.h"
int k;

编译时会产生错误

Error   1   error LNK2005: "int k" (?a@@3HA) already defined in A.obj

我不明白发生了什么。

有人可以向我解释一下吗?

4

8 回答 8

103

为什么会出现这个错误?

您违反了一个定义规则,因此出现了链接错误。

建议的解决方案:


如果您在两个 cpp 文件中需要相同的命名变量,那么您需要使用无名命名空间(匿名命名空间)来避免错误。

namespace 
{
    int k;
}

如果您需要在多个文件之间共享相同的变量,那么您需要使用extern.

extern int k;

A.cpp

#include "A.h"
int k = 0;

B.cpp

#include "A.h"

//Use `k` anywhere in the file 
于 2012-04-06T16:50:03.753 回答
83

在项目的设置中,添加 /FORCE:MULTIPLE到链接器的命令行选项。

来自MSDN:“使用 /FORCE:MULTIPLE 创建一个输出文件,无论 LINK 是否为一个符号找到多个定义。”

于 2013-11-02T10:19:48.610 回答
15

如果您希望两者都引用同一个变量,其中一个应该有int k;,另一个应该有extern int k;

对于这种情况,您通常将定义 ( int k;) 放在一个.cpp文件中,并将声明 ( extern int k;) 放在标题中,以便在您需要访问该变量的任何地方包含。

如果您希望每个k变量都是恰好具有相同名称的单独变量,则可以将它们标记为static,例如:(static int k;在所有文件中,或至少在一个文件中)。或者,您可以使用匿名命名空间:

namespace { 
   int k;
};

同样,除了最多一个文件之外的所有文件。

在 C 中,编译器通常对此并不那么挑剔。具体来说,C 有一个“暂定定义”的概念,所以如果你有int k;两次类似的东西(在相同或不同的源文件中),每个都将被视为一个暂定定义,它们之间不会发生冲突。但是,这可能有点令人困惑,因为您仍然不能有两个都包含初始化程序的定义——带有初始化程序的定义始终是完整定义,而不是暂定定义。换句话说,int k = 1;出现两次是错误的,但int k;在一个地方和int k = 1;另一个地方不会。在这种情况下, theint k;将被视为暂定定义,而the 将被视为int k = 1;定义(并且两者都引用相同的变量)。

于 2012-04-06T16:52:41.463 回答
6

假设您希望 'k' 在不同的 .cpp 文件中成为不同的值(因此声明了两次),请尝试将两个文件更改为

namespace {
    int k;
}

这保证了名称“k”在翻译单元中唯一标识“k”。旧版本static int k;已弃用。

如果您希望它们指向相同的值,请将其更改为extern int k;.

于 2012-04-06T16:50:30.610 回答
6

两个文件都将变量定义k为整数 ( int)。

结果,链接器看到两个具有相同名称的变量,如果您曾经引用k​​ .

要解决此问题,请将其中一个声明更改为:

extern int k;

这意味着:“k 是一个整数,在这里声明,但在外部定义(即另一个文件)。”

现在只有一个变量k,可以被两个不同的文件正确引用。

于 2012-04-06T16:54:08.530 回答
3

如果你想让这些翻译单元共享这个变量,在 A.cpp 中定义int k;并放入extern int k;B.cpp。

于 2012-04-06T16:52:13.760 回答
3

头文件中的存在int k;会导致在包含此头文件的每个翻译单元中定义符号k,而链接器希望它只定义一次(又名一个定义规则违规)。

虽然涉及extern的建议没有错,但extern它是一种 C 主义,不应使用。

允许在多个翻译单元中定义头文件中的变量而不导致 ODR 违规的 C++17 前解决方案将转换为模板:

template<typename x_Dummy = void> class
t_HeaderVariableHolder
{
    public: static int s_k;
};

template<typename x_Dummy> int t_HeaderVariableHolder<x_Dummy>::s_k{};

// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
    return t_HeaderVariableHolder<>::s_k;
}

使用 C++17,事情变得更加简单,因为它允许inline变量:

inline int g_k{};

// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
    return g_k;
}
于 2019-04-12T20:59:40.310 回答
1

链接器告诉您该变量k定义了多次。实际上,您在 A.cpp 中有一个定义,在 B.cpp 中有另一个定义。两个编译单元都会生成相应的目标文件,链接器使用该目标文件来创建您的程序。问题是,在您的情况下,链接器不知道k要使用哪个定义。在 C++ 中,同一构造(变量、类型、函数)只能有一个定义。

要解决它,你必须决定你的目标是什么

  • 如果你想有两个变量,都命名k,你可以在两个 .cpp 文件中使用匿名命名空间,然后k像现在一样引用:

.

namespace {
  int k;
}
  • 您可以将其中一个ks 重命名为其他名称,从而避免重复定义。
  • 如果您只想k在两个 .cpp 文件中定义并使用它,则需要在一个中声明为extern int k;,并在另一个中保留原样。这将告诉链接器在两种情况下都使用一个定义(未更改的版本)——extern意味着该变量是在另一个编译单元中定义的。
于 2012-04-06T16:54:46.213 回答