错误 LNK2005,已经定义?

我有两个文件 A.cpp 和 B.cpp 在一个 Win32控制台应用中。

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

#include "stdafx.h"
int k;

编译时会产生错误

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

我不明白发生了什么。

有人能给我解释一下吗?

218855 次浏览

为什么是这个错误?

您破坏了 一个定义规则,因此产生了链接错误。

建议解决方案:


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

namespace
{
int k;
}

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

extern int k;

心肺复苏术

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

心肺复苏

#include "A.h"


//Use `k` anywhere in the file

假设你想让‘ k’在不同的。Cpp 文件(因此声明它两次) ,尝试将两个文件都更改为

namespace {
int k;
}

这保证了名称‘ k’在翻译单元之间唯一标识‘ k’。

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

如果希望这些翻译单元共享这个变量,可以在 A.cpp 中定义 int k;,并将 extern int k;放在 B.cpp 中。

如果希望两者引用同一个变量,其中一个应该有 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;在另一个不会。在这种情况下,int k;将被视为一个暂定的定义,而 int k = 1;将被视为一个定义(两者指的是同一个变量)。

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

因此,链接器看到两个具有相同名称的变量,并且不确定如果您曾经引用 k,它应该使用哪一个。

为了解决这个问题,将声明的 更改为:

extern int k;

意思是: K 是一个整数,在这里声明,但在外部定义(即另一个文件)

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

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

要解决这个问题,你必须决定你的目标是什么

  • 如果希望同时具有两个名为 k的变量,则可以在这两个变量中都使用匿名命名空间。Cpp 文件,然后像现在这样引用 k:

.

namespace {
int k;
}
  • 您可以将其中一个 k重命名为其他名称,从而避免重复定义。
  • 如果您希望只有一次 k的定义,并在两者中都使用它。Cpp 文件中,您需要在其中一个文件中声明为 extern int k;,并保持它在另一个文件中的样子。这将告诉链接器在两种情况下都使用一个定义(未改变的版本)—— extern意味着变量是在另一个编译单元中定义的。

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

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

在头文件中出现 int k;会导致在每个翻译单元中定义符号 k,这个头文件被包括在内,而链接器期望它只被定义一次(又名一个违反定义规则)。

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

前 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;
}