错误LNK2005,已定义

error LNK2005, already defined?

本文关键字:定义 LNK2005 错误      更新时间:2023-10-16

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

这两个文件都仅包含以下 2 行代码:

#include "stdafx.h"
int k;

编译时会产生错误

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

我不明白发生了什么。

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

为什么会出现此错误?

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

建议的解决方案:


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

namespace 
{
    int k;
}

如果需要在多个文件之间共享同一变量,则需要使用 extern

extern int k;

答.cpp

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

B.cpp

#include "A.h"
//Use `k` anywhere in the file 

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

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

如果希望两者都引用相同的变量,则其中一个应该具有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"在不同的.cpp文件中成为不同的值(因此声明两次),请尝试将两个文件更改为

namespace {
    int k;
}

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

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

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

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

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

extern int k;

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

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

如果您希望

这些翻译单元共享此变量,请在 A 中定义int k;.cpp并将extern int k;放在 B 中.cpp。

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

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

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

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

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

要修复它,您必须决定您的目标是什么

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

.

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