1

我无法理解头球和头球后卫。我已经阅读了其他问题及其答案,但我仍然无法在 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:函数 _wmain c:\Users\Danny\documents\visual studio 2013\Projects\Addition Program\main 中引用的未解析外部符号“int __cdecl add(int,int)”(?add@@YAHHH@Z) \main.obj 主要

错误 2 error LNK1120: 1 unresolved externals c:\users\danny\documents\visual studio 2013\Projects\Addition Program\Debug\main.exe main


1. 头球和头球后卫是如何工作的?我看到 #include 是如何add.h知道main.cpp的声明的add(int x, int y),但是它是如何找到它的定义的呢?


2. 我的代码有什么问题?

我的代码现在正在编译。我的代码没有编译的原因是因为我一直在File > New > File...将文件添加到我的项目中,而不是通过Visual Studio中解决方案资源管理器的Source FilesHeader Files部分添加它们。我还需要添加到 add.cpp 文件中。#include "stdafx.h

4

2 回答 2

5

可以这样想:每个.cpp文件都经过预处理,然后与其他文件完全分开编译。

所以让我们先预处理main.cpp。这涉及查看以 . 开头的所有行#。该文件main.cpp只有#include行,它们只是复制它们所包含的文件的内容。我将用注释来表示stdafx.hand的内容iostream,但实际上我会复制add.hin 的内容:

// 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将被丢弃。

这非常重要,因为对一个函数(以及许多其他东西)进行多个定义会给你一个错误。

于 2014-01-31T19:13:05.850 回答
1

IDE 依次编译每个 .cpp 文件以生成该特定文件的目标文件(机器代码)。

完成所有这些后,它会将这些位写入以形成可执行文件。IDE 知道什么需要与什么相关联。

这是一个有点简单的答案

于 2014-01-31T19:05:14.050 回答