【问题标题】:error LNK2005, already defined?错误 LNK2005,已定义?
【发布时间】:2012-04-06 16:47:32
【问题描述】:

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

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

#include "stdafx.h"
int k;

编译时会报错

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

我不明白发生了什么。

谁能给我解释一下?

【问题讨论】:

  • 天电集团?是你公司的名字吗?当您提出基本问题时,我不建议您使用贵公司的名称签字。特别是如果您的客户希望您保护他们的某种信息。
  • 提示:缺少的#include "stdafx.h" 也是此错误的根源。

标签: c++


【解决方案1】:

为什么会出现这个错误?

您破坏了 one definition rule 并因此导致链接错误。

建议的解决方案:


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

namespace 
{
    int k;
}

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

啊.h

extern int k;

A.cpp

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

B.cpp

#include "A.h"

//Use `k` anywhere in the file 

【讨论】:

  • 如果是externk 应该在任何源文件中定义一次。
  • 我也遇到了这个问题,但我没有重新定义它,我尝试在另一个 .dll 项目中使用我的 .dll 来为它创建一个托管包装器。有什么建议吗?
  • 在任何header文件中定义 k int k = 0;时都会弹出同样的错误。
【解决方案2】:

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

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

【讨论】:

  • 这是唯一解决方案,当链接器或预处理器无法看到该事物仅被定义一次!必须也是VS中的一些错误。我只是想知道它是如何为这么多人工作这么长时间的。 (我这里指的不是Q,而是来自外太空的链接器错误,我猜...)
  • 我感觉这是一种“蛮力”的做法,并没有真正解决原来的问题;它使我的一半函数指向空值。由于这解决了我的问题,因此我给出了 +1 的答案。
  • 想指出,如果您使用新编译器编译使用旧编译静态库的代码库,这可能是解决此问题的唯一方法。
  • 这适用于与 C 运行时库略有不同的链接问题。我在多个目标文件中获得了数学库函数的重复静态分辨率。
  • 虽然这个答案不是 OP 问题的正确答案,但我赞成,因为这对我有用。我收到此错误是因为我包含了我正在为其编写单元测试的模块的头文件,该模块又包含 wx/string.h(但我的单元测试不使用 wxString)。单元测试编译为一个单独的项目,因此 wxString::~wxString 被定义在两个 .obj 文件中。这个解决方案允许我继续编写单元测试,而不必完全重构我正在维护的代码(这需要重构,但这需要一些时间)。
【解决方案3】:

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

【讨论】:

  • +1 但是杰瑞,如果你能添加一些关于 C 的话会很好。我想也许 OP 来自 C,它的“暂定”声明...... C/C++ 的区别让我困惑了一段时间。
  • 当我只定义了一次变量时,静态关键字帮助了我。谢谢!
【解决方案4】:

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

namespace {
    int k;
}

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

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

【讨论】:

    【解决方案5】:

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

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

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

    extern int k;
    

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

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

    【讨论】:

    • 你能在多个地方引用变量吗(即没有extern)?
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

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

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

      允许头文件中的变量在多个翻译单元中定义而不会导致 ODR 冲突的 Pre 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;
      }
      

      【讨论】:

      • 这是什么?我希望看到的头文件中的普通变量在哪里? g_k 之后的 {} 是什么?如何在不违反 ODR 的情况下在头文件中设置具有值的全局变量?
      • @gil123 {}list initialization syntax。空列表将 int 初始化为 0,同时可以提供一些初始值,例如 inline int g_k{1234};。由于链接器处理模板和内联变量的方式,不会出现 ODR 违规。在这种情况下,链接器将只选择一个定义来形成结果程序,而不是抱怨它已经在其他地方定义了。
      • C++ 花了很长时间才以一种简单的方式实现了如此多的基本功能——在头文件中允许全局变量带值。它是如此连线,以至于以前以简单的方式不允许这样做。起作用的是inline 关键字。我还是不明白你为什么不直接写inline int g_k = 0;?我注意到此语法将使用值0 初始化变量。为什么使用列表语法只设置一个值?为什么它比使用 = 运算符更好?
      【解决方案8】:

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

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

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

      .

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

      【讨论】:

        猜你喜欢
        • 2012-04-25
        • 2010-10-11
        • 2015-08-10
        • 1970-01-01
        • 2014-12-21
        • 2022-01-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多