我无法理解标题和标题后卫。我已经阅读了其他问题及其答案,但我仍然无法在Visual Studio 2013中完成这项工作:
main.cpp
#include "stdafx.h"
#include <iostream>
#include "add.h"
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
add.cpp
#include "stdafx.h" //ADDED LATER; NOW WORKING (AND SEE QUESTION 2 BELOW)
#include "add.h" //ADDED LATER; NOR WORKING (AND SEE QUESTION 2 BELOW)
int add(int x, int y) {
return x + y;
}
add.h
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
编译时,控制台窗口在屏幕上闪烁然后消失。错误列表包含:
错误1错误LNK2019:未解析的外部符号“int __cdecl 函数中引用的add(int,int)“(?add @@ YAHHH @ Z) _wmain c:\ Users \ Danny \ documents \ visual studio 2013 \ Projects \ Addition Program \ main \ main.obj main
错误2错误LNK1120:1未解决 外部c:\ users \ danny \ documents \ visual studio 2013 \ Projects \ Addition Program \ Debug \ main.exe main
add.h
,它使main.cpp
知道add(int x, int y)
的声明,但是它如何找到它的定义?我的代码现在正在编译。我的代码没有编译的原因是因为我一直在文件&gt;新&gt;文件... 将文件添加到我的项目中,而不是通过解决方案资源管理器<的源文件和页眉文件部分添加文件/ strong>在Visual Studio中。我还需要将#include "stdafx.h
添加到add.cpp文件中。
答案 0 :(得分:5)
以这种方式思考:每个.cpp
文件都经过预处理,然后与其他文件完全分开编译。
所以让我们先预处理main.cpp
。这涉及查看以#
开头的所有行。文件main.cpp
只有#include
行,只是复制他们所包含文件的内容。我将使用评论代表stdafx.h
和iostream
的内容,但我实际上会将add.h
的内容复制到:
// Contents of stdafx.h
// Contents of iostream
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
现在看到add.h
的内容已被纳入main.cpp
?事实上,它带来了一些更多的预处理器指令,所以我们需要做他们说的话。第一次检查ADD_H
是否尚未定义(在此文件中),它不是,因此将所有内容保留到#endif
:
// Contents of stdafx.h
// Contents of iostream
#define ADD_H
int add(int x, int y);
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
现在剩下的预处理程序指令定义了ADD_H
,我们留下了最后的翻译单元:
// Contents of stdafx.h
// Contents of iostream
int add(int x, int y);
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
现在可以编译这个文件了。如果调用类似add
的函数,编译器只需要能够看到该函数的声明,以便成功编译。预计该函数将在某些其他翻译单元中定义。
现在让我们来看看预处理add.cpp
。事实上,add.cpp
没有任何预处理指令,因此不需要发生任何事情。通常情况下,您会#include "add.h"
,但如果不这样做,您的程序仍会编译。所以在预处理后我们仍然有:
int add(int x, int y) {
return x + y;
}
然后编译,我们现在有add
函数的定义。
在编译完所有.cpp
个文件后,它们将链接。链接器负责查看已编译的main.cpp
使用函数add
,因此查找其定义。它在编译的add.cpp
中找到定义并将它们链接在一起。
然后你可能想知道我们为什么要包括警卫。在这个例子中,它似乎毫无价值。没错,在这个例子中它没有任何用处。包含防护是为了防止在单个文件中包含两次相同的标头。当您拥有更复杂的项目结构时,这很容易发生。但是,让我们看一下main.cpp
包含add.h
两次的不切实际的例子:
#include "stdafx.h"
#include <iostream>
#include "add.h"
#include "add.h"
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
预处理可以为您提供:
// Contents of stdafx.h
// Contents of iostream
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
第一个#ifndef
将是流程,它会看到ADD_H
尚未定义,直到#endif
的所有内容都将保留。然后定义ADD_H
。
然后处理了第二个#ifndef
,但此时ADD_H
已经定义,所以直到#endif
的所有内容都将被丢弃。
这非常重要,因为对函数(以及许多其他事情)进行多次定义会给您带来错误。
答案 1 :(得分:1)
IDE依次编译每个.cpp文件,以生成该特定文件的目标文件(机器代码)。
完成所有这些后,它会将这些位写入以形成可执行文件。 IDE知道什么需要与什么相关联。
这是一个有点简单的anwser