c – 如何解释LNK2005?

所以,有人来找我的项目无法链接错误LNK2005:已在对象中定义的符号(使用Visual Studio 2010).在这种情况下,我知道什么是错的(因此可以指出它们是正确的解决方案),但是我不知道为什么这在一个级别上是错误的,以便给出一个很好的解释(以防止它再次发生).

// something.h
#ifndef _SOMETHING_H
#define _SOMETHING_H
int myCoolFunction();

int myAwesomeFunction() // Note implementing function in header
{
    return 3;
}
#endif

// something.cpp
#include "something.h"
int myCoolFunction()
{
    return 4;
}

// main.cpp
#include <iostream>
#include "something.h"

int main()
{
    std::cout << myAwesomeFunction() << std::endl;
}

这会导致链接失败,并通过将myAwesomeFunction()放入.cpp并在.h中留下声明来修复.

我对链接器如何工作的理解几乎从here开始.据我所知,我们提供了一个在一个地方需要的符号.

我查了一下MSDN article on LNK2005,它与我期望链接器的行为方式相符(不止一次提供符号 – >链接器很混乱),但似乎没有涵盖这种情况(这意味着我不理解关于链接的一些明显的东西) ).

Google和StackOverflow会与不包含#ifndef或#pragma一次的人产生问题(导致提供符号的多个声明)

A related question I found on this site有同样的问题,但答案并没有解释为什么我们将这个问题充分地理解到我的理解水平.

我有一个问题,我知道解决方案,但我不知道为什么我的解决方案有效

最佳答案 在典型的C项目中,您可以单独编译每个实现(或.cpp)文件 – 通常不会直接将头文件(或.h)文件传递给编译器.在执行所有预处理和包含之后,这些文件中的每一个都成为翻译单元.所以在你给出的例子中,有两个翻译单元看起来像这样:

> main.cpp翻译单位:

// Contents of <iostream> header here

int myCoolFunction();

int myAwesomeFunction() // Note implementing function in header
{
    return 3;
}

int main()
{
    std::cout << myAwesomeFunction() << std::endl;
}

> something.cpp翻译单位:

int myCoolFunction();

int myAwesomeFunction() // Note implementing function in header
{
    return 3;
}

int myCoolFunction()
{
    return 4;
}

请注意,这两个翻译单元都包含重复内容,因为它们都包含something.h.如您所见,上述翻译单元中只有一个包含myCoolFunction的定义.非常好!但是,它们都包含myAwesomeFunction的定义.那很糟!

在单独编译翻译单元之后,将它们链接以形成最终程序.关于翻译单元的多个声明有一些规则.其中一条规则是(§3.2/ 4):

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

您的程序中有myAwesomeFunction的多个定义,因此您违反了规则.这就是您的代码无法正确链接的原因.

您可以从链接器的角度来考虑它.编译完这两个翻译单元后,您有两个目标文件.链接器的工作是将目标文件连接在一起以形成最终的可执行文件.因此它在main中看到对myAwesomeFunction的调用,并尝试在其中一个目标文件中查找相应的函数定义.但是,有两种定义.链接器不知道使用哪一个,所以它只是放弃了.

现在让我们看看如果你在something.cpp中定义myAwesomeFunction,那么翻译单元会是什么样子:

>修复了main.cpp翻译单元:

// Contents of <iostream> header here

int myCoolFunction();

int myAwesomeFunction();

int main()
{
    std::cout << myAwesomeFunction() << std::endl;
}

>修复了something.cpp翻译单位:

int myCoolFunction();

int myAwesomeFunction();

int myCoolFunction()
{
    return 4;
}

int myAwesomeFunction()
{
    return 3;
}

现在它很完美.现在整个程序中只有myAwesomeFunction的一个定义.当链接器在main中看到对myAwesomeFunction的调用时,它确切地知道应该将它链接到哪个函数定义.

点赞