对错误LNK2005的探究

877 阅读3分钟

用vs2019写代码的时候出现链接器工具错误 LNK2005提示。网上有两种方法[1]:

  1. 换IDE,不用visual studio;
  2. 在link commandline加上一行 /FORCE:MULTIPLE;简单说就是VS的STL从import变成static了。

但实际上是治标不治本的方法,遂进行如下探究。

错误样式

错误 LNK2005 "void __cdecl Merge(class std::vector<int,class std::allocator<int> > &,int,int,int)" (?Merge@@YAXAAV?$vector@HV?$allocator@H@std@@@std@@HHH@Z) 已经在 MergeSort.obj 中定义

错误样式示例

代码样式示例

  • A.cpp
A()//定义
  • B.cpp
#include"A.cpp"

main()
{
  A();
}

原因

官方说法:通常情况下,此错误意味着必须要断开定义的一个规则,这允许跨整个可执行文件只有一个定义为任何使用的模板、 函数、 类型或对象在给定的对象文件中,并只有一个定义外部可见的对象或函数[2]。

那么本处出现的问题为:编译器不知道A函数的存在!就相当于知道一个班,但是不知道小A在这个班。

我们可以看看声明与定义的作用[3]:

  • 声明的意义是让编译器知道其存在,重复的声明是可以的;
  • 定义的意义是让编译器为其分配空间,或说创建对象,重复的定义是禁止的。

编译器的编译单元是各个c/cpp源文件,而在每个编译单元中所使用到的变量和函数,编译器必须要知道其存在及其存在的形式。所以说所有涉及到的对象必须有声明存在于每个源文件中,但是定义可以放在其他源文件中,编译器会自动在最后链接。

解决办法

在A.cpp文件中调用B.cpp的办法[4]:

  1. 在B.cpp中有完整的函数定义,在A.cpp中添加一下要用到的函数声明。
  2. 把B.cpp中完整定义的函数的原型,写到一个B.h文件中,然后在A.cpp的文件头增加#include"B.h"。

扩展:c头文件(.h)的作用[5]

.h中一般放的是同名.c文件中定义的变量、数组、函数的声明,需要让.c外部使用的声明。

  • .h文件作用:

    1. 方便开发:包含一些文件需要的共同的常量,结构,类型定义,函数,变量申明;
    2. 使函数的作用域从函数声明的位置开始,而不是函数定义的位置(实践总结);
    3. 提供接口:对一个软件包来说可以提供一个给外界的接口(例如: stdio.h)。
  • h文件里应该有什么:常量,结构,类型定义,函数,变量申明。

  • h文件不应该有什么:变量定义, 函数定义。

参考

[1] hongbin_xu. class std::vector. blog.csdn.net/hongbin_xu/…
[2] 链接器工具错误 LNK2005. docs.microsoft.com/zh-cn/cpp/e…
[3] FreakZhang. C/C++ 声明定义作用域链接. www.jianshu.com/p/cb96a82c1…
[4] GoodPanpan. 如何在c语言中源文件调用另一个源文件的函数. www.cnblogs.com/lpworkstudy…
[5] zqixiao_09. c头文件(.h)的作用. blog.csdn.net/zqixiao_09/…