多重定义符号的链接器错误

时间:2016-08-10 02:10:09

标签: c

我有以下文件结构:

X.h

#pragma once

#include "Y.h"

int ONE = 1;

int combine();

X.c

#include "X.h"

int combine()
{
    return ONE + TWO;
}

Y.h

#pragma once

int TWO = 2;

Y.c

#include "Y.h"

MAIN.C

#include "X.h"

int main()
{
    int fusion = combine();

    return 0;
}

我收到以下错误:

LNK1169 one or more multiply defined symbols found

LNK2005 _ONE already defined in Main.obj

LNK2005 _TWO already defined in Main.obj

KLNK2005 _TWO already defined in Main.obj

这没有任何意义。如果我们从Main.c开始,编译器必须包含X.h。然后,编译器查找与X.h关联的C文件。在X.c内,它需要包含X.h,但#pragma once 应该防范。然后它需要包含Y.h。它查找C文件并查找Y.c,其中包含Y.h,但Y.h已包含在内。然后,它返回到Main.c并且应该成功编译...但是没有。

我可以将/FORCE添加到我的项目设置中,这使我的代码运行完美但仍然输出:

ONE has already been defined, second definition ignored.

3 个答案:

答案 0 :(得分:5)

  

但是#pragma once应该防范

#pragma once用于避免重复,包括单个翻译单元(在这种情况下为.c文件),但它无法阻止多个定义跨越多个翻译单元。

您在ONE文件中定义了全局变量TWO.h,并且它们包含在多个翻译单元中并导致多个定义错误。

例如,最后,ONE将在X.cMain.c中定义,TWO将在Y.cX.c中定义, Main.c

您应在ONE文件中声明TWO.h(使用extern),并在.c文件中对其进行定义。

X.h

#pragma once

#include "Y.h"

extern int ONE;
int combine();

X.c

#include "X.h"

int ONE = 1;

int combine()
{
    return ONE + TWO;
}

Y.h

#pragma once

extern int TWO;

Y.c

#include "Y.h"

int TWO = 2;

答案 1 :(得分:1)

#pragma once仅防止在同一编译单元中包含两次相同的标头。在多个编译单元中定义相同的变量时,它不会阻止问题。

要了解错误的原因,让我们逐步了解每个编译单元在编译时的样子。

预处理后,X.c最终看起来像这样:

int TWO = 2;

int ONE = 1;

int combine();

int combine()
{
    return ONE + TWO;
}

如您所见,它包含符号TWOONEcombine的定义。

Y.c最终看起来像这样:

int TWO = 2;

它还定义了符号TWO

main.c看起来像这样:

int TWO = 2;

int ONE = 1;

int combine();

int main()
{
    int fusion = combine();

    return 0;
}

因此,它还包含TWOONE以及main的定义。

因此,在编译之后,我们最终得到3个目标文件,其中三个都有TWO的定义,其中两个定义了ONE。当我们进行链接时,链接器会在ONE的定义中看到TWOcombine的引用,并寻找这些符号'定义。由于它们在多个地方定义,因此会抛出错误并放弃。

您可以通过在Xh和Yh中声明 ONETWO以及定义 ONE和{{1}来解决此问题在Xc和Yc中

答案 2 :(得分:0)

#pragma once可防止在编译单个源模块时多次包含头文件。

这就是全部。

您的头文件包含在两个不同的源模块中。

该头文件在全局范围内声明并定义一个对象。

因此,两个源模块最终都会在全局范围内定义相同的对象。

重复符号定义的来源。

#pragma once不能代替了解和理解C ++范围规则。