错误LNK2005,已定义?

时间:2012-04-06 16:47:33

标签: c++

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

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

#include "stdafx.h"
int k;

编译时会产生错误

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

我不明白发生了什么。

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

8 个答案:

答案 0 :(得分:90)

为什么会出现此错误?

你打破了 one definition rule ,从而导致链接错误。

建议的解决方案:


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

namespace 
{
    int k;
}

如果您需要在多个文件中共享相同的变量,则需要使用extern

<强> A.H

extern int k;

<强> A.cpp

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

<强> B.cpp

#include "A.h"

//Use `k` anywhere in the file 

答案 1 :(得分:67)

将/ FORCE:MULTIPLE添加到链接器命令行选项。

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

答案 2 :(得分:13)

如果您希望两者都引用同一个变量,其中一个应该有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;将被视为定义(并且都将引用相同的变量)。

答案 3 :(得分:6)

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

namespace {
    int k;
}

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

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

答案 4 :(得分:6)

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

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

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

extern int k;

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

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

答案 5 :(得分:3)

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

答案 6 :(得分:2)

头文件中int k;的存在会导致将符号k定义在包含此头的每个转换单元中,而链接器希望仅定义一次(即违反“一个定义规则”)。

虽然涉及extern的建议没有错,但extern是一种C-ism,因此不应使用。

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

答案 7 :(得分:1)

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

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

  • 如果您想要两个名为k的变量,您可以在两个.cpp文件中使用匿名命名空间,然后像现在一样参考k

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