c++ 错误LNK2005,是否已定义?

n53p2ov0  于 2023-11-19  发布在  其他
关注(0)|答案(9)|浏览(160)

我有2个文件,A.cpp和B.cpp,在Win32控制台应用程序。
这两个文件都只包含以下两行代码:

#include "stdafx.h"
int k;

字符串
编译时,它会产生错误

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


我不明白这是怎么回事。
有人能给我解释一下吗?

rta7y2nd

rta7y2nd1#

为什么会出现这个错误?

你破坏了**one definition rule**,因此导致了链接错误。

建议解决方案:

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

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

368yc8dk

368yc8dk2#

在项目的设置中,将/FORCE:MULTIPLE添加到链接器的命令行选项中。
来自MSDN:“使用/FORCE:MULTIPLE创建输出文件,无论LINK是否为符号找到多个定义。”

plupiseo

plupiseo3#

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

u2nhd7ah

u2nhd7ah4#

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

namespace {
    int k;
}

字符串
这保证了名称“k”在翻译单元中唯一标识“k”。旧版本static int k;已弃用。
如果你想让它们指向相同的值,可以将其中一个改为extern int k;

hjzp0vay

hjzp0vay5#

这两个文件都将变量k定义为整数(int)。
因此,链接器会看到两个同名的变量,并且不确定如果引用k时应该使用哪一个。
要解决这个问题,请将其中 one 声明更改为:

extern int k;

字符串
这意味着:*“k是一个整数,在这里声明,但在外部定义(即另一个文件)。
现在只有一个变量k,可以被两个不同的文件引用。

s4chpxco

s4chpxco6#

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

ryoqjall

ryoqjall7#

头文件中存在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;
}

erhoui1w

erhoui1w8#

链接器告诉您已经多次定义了变量k,您在A.cpp中有一个定义,在B.cpp中有另一个定义。这两个编译单位都会产生链接器用来建立程式的Map目的档。问题是,在您的案例中,链接器不知道要使用k的哪一个定义。在C++中同一个构造(变量、类型、函数)只能有一个定义。
要解决这个问题,你必须决定你的目标是什么

  • 如果您想要有两个变数,都命名为k,您可以在两个.cpp档案中使用匿名命名空间,然后参照k,就像您现在所做的一样:

namespace {
  int k;
}

字符串

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

23c0lvtd9#

正如之前在社区中提到的,这基本上是由在头文件中实现定义并在项目中多次包含它引起的。为了解决这个问题,我在变量和函数/方法的定义之前使用了内联,它工作得很好。

//in the header file:
inline int k{123};

字符串

相关问题