标题和未解析的外部符号

时间:2014-06-13 07:46:40

标签: c++

我刚刚开始学习C ++,无法理解我需要如何使用头文件。我已经看到并阅读了一些关于Stack Overflow的现有答案,但仍然无法理解。我写了这段代码,它给了我Unresolved Externals错误。

Sum.h

#ifndef SUM
#define SUM

int add(int x, int y, int z);

#endif

Sum.cpp

#include <iostream>
#include "Sum.h"

int add(int x, int y, int z)
{
    return x + y + z;
}

Main.cpp的

#include <iostream>
#include "Sum.h"

int main()
{
    using namespace std;
    cout << "3 + 4 + 5 = " << add(3, 4, 5) << endl;
    return 0;
}

如何解决此问题,以便我的代码可以运行?

谢谢!

编辑:忘记添加,我正在使用Microsoft Visual Studio Express 2013 for Windows Desktop。

1 个答案:

答案 0 :(得分:3)

我假设您正在尝试运行这样的程序:

g++ main.cpp
./a.out

问题是,即使你的程序会编译,它也不会链接&#34;。发生的事情是链接器无法找到&#34; add(int,int,int)&#34;的代码。要解决此问题,您需要指定编译中的所有文件。试试这个:

g++ main.cpp sum.cpp
./a.out

在第一种情况下,虽然您的sum.h文件是直接包含的,但是有一个预处理器规范要求导入它。然后,main.cpp编译,但输出不包含add函数的代码。输出只是说&#34;在这些数字上调用一个名为add的函数&#34;。编译器告诉链接器,当最终将函数全部转换为可执行文件时,链接器负责找到该函数。然后当链接器运行时,它无法找到任何方法&#34;添加&#34;其中&#34; main.cpp&#34;的输出指的是。结果,它会抛出错误。

在第二种情况下,两个文件都被编译(通常是.o文件),其中包含其中包含的所有文件的代码。现在,当链接器尝试将其全部链接时,它可以轻松找到add方法,因为它具有要使用的.o文件。试试这个可以帮助您了解此过程的工作原理:

g++ -c main.cpp

这会将main.cpp编译为main.o文件。在目录中执行ls以查看此文件。 -c选项要求编译器跳过链接过程并仅编译源文件。然后,这样做:

g++ -c sum.cpp

如果你再看看,你会发现有一个名为sum.o的文件。因此,您基本上已将文本中的代码转换为二进制,这是一种中间格式。大多数验证,类型检查,语法检查等已经执行,main.osum.o是有效的文件,表示执行某个计算的步骤。请注意,编译步骤不需要main方法。它只是将指令从一种格式转换为另一种格式。在构建可执行文件时需要它,因为您当然需要知道在运行可执行文件时应该执行什么。要获得最终的可执行文件,您只需要执行此操作:

g++ -o myexec.out sum.o

哎呀..你在这里看到一个错误。它说什么? undefined reference to main。这证实了我们之前的想法,当我们说链接器需要一个&#34; main&#34;链接到的函数,否则它不知道运行可执行文件时从哪里开始。让我们给它一个主要功能:

g++ -o myexec.out sum.o main.o

而且..你去..编译。 -o开关只是告诉g++应该调用输出可执行文件。要运行您的程序,只需执行以下操作:

./myexec.out

这就是全部......一旦你对此感到满意,你就想进入Makefile和autotools。现实生活中的项目有成千上万个C ++文件,这些文件以类似的方式编译,但是一旦你进入两位数的文件编号就很难管理。因此,我们使用make之类的工具来简化此过程并使其具有高度可配置性。希望这有助于您开始!

修改#2

我以为我会很快放弃对.h文件的需求。编译器一次只处理文件。因此,当它处理main.cpp时,它遇到了一个名为add(4, 5, 1)的东西,它不知道add是什么。据它所知,它可能是一个变量int add = 10而我会错误地使用它。编译器如何验证这一点? sum.h文件可以帮助它,并在此过程中帮助我们。 .h文件只告诉编译器符号add是什么类型。如果您尝试创建另一个名为sum2.cpp的文件,并在其中定义添加为add(int x, int y)而不是3变量函数,则两个文件都将编译文件,但链接仍将失败。这是因为链接器正在寻找符号add(int, int, int),而它看到的是add(int, int)。编译器只根据你抛出的定义来验证你的代码。

如果您要从第一行删除#include "sum.h"语句,只需将其替换为int add(int x, int y, int z);,并按照上面所述的所有步骤操作,它仍然有效。但是,我们在单独的文件中定义它的原因是我们更容易管理变更。在这种情况下,即使sum.cpp和main.cpp分别进行编译,它们也是根据相同的定义进行编译的,这使我们都得到了检查。只有一个定义的位置,它充当了真理的来源,并确保sum.cpp和main.cpp都是同步的。