【问题标题】:I am struggling to understand preprocessing, compiling and linking [duplicate]我正在努力理解预处理、编译和链接 [重复]
【发布时间】:2018-11-30 11:57:44
【问题描述】:

我正在阅读一本名为 jumping into C++ 的书,书中有一部分是这样说的:

比如这样的语句

#include <iostream>

告诉预处理器将文件 iostream 的文本直接抓取到当前文件中。每次包含头文件时,它都会在编译器看到它之前被粘贴到文件中,并且#include 指令将被删除。

因此,我了解到所有文件都粘贴到一个文件中,因此您只剩下一个源文件。但他接着说:

每个源文件都是单独编译的,这意味着目标文件仅包含已编译的源代码文件的机器语言。

这表明源文件在进入编译阶段时仍然是分开的,但我认为所有文件都已粘贴到一个大文件中。有人可以解释一下,是所有文件都粘贴到一个文件中,还是分开后由链接器连接?

【问题讨论】:

  • "所有文件都已粘贴" - 所有包含文件都粘贴到称为翻译单元。具有不同包含文件堆栈的不同源文件将被视为不同的翻译单元。编译每个翻译单元,然后将每个翻译单元生成的目标代码链接在一起以形成您的程序。
  • 所以有一个类似于主文件的源文件,你的程序中有多个源文件,每个源文件包含不同的小文件?如果是这样,那么是什么将某些东西定义为源文件而不是包含在源文件中的较小文件?
  • @AmeenIzhac 你所说的部分正确。是的,有一个“大老板”源文件。一个入口点,所有事情都从这里开始。默认情况下它的名字main.cpp,必须是这个名字,否则编译器不知道要编译什么并且可能有can't find main错误(你可以通过设置来改变它)。我认为您可能需要阅读更多关于 C++ 的内容并查看更多示例。

标签: c++ compilation preprocessor


【解决方案1】:

你的最后一点是正确的。 (以下文件结尾适用于 Linux 系统)

在预处理器完成其工作后,编译器将每个源文件 (.cpp) 编译成一个单独的目标文件 (.o)。

然后,链接器将它们组合成可执行文件、共享库 (.so) 或静态库 (.a)。

查看这个问题了解更多信息:How does the compilation/linking process work?

【讨论】:

  • 谢谢,但我的问题是,如果所有文件都已粘贴到预处理器中的单个文件中,编译器如何将“每个源文件编译为一个单独的目标文件”
  • @AmeenIzhac 仅将头文件和预处理器命令粘贴到源文件中,而不是其他文件的源中。示例:源文件 A 和 B 以及两者都使用的头文件 C,结果为 CA 和 CB,而不是 int CAB。现在 CA 和 CB 可以完全分开编译了。
  • 所以你说的是源文件和头文件是有区别的;源文件就像主要文件之一,而头文件是您创建的要包含在源文件中的较小文件?如果我刚才说的是正确的,那么将某些东西定义为源文件而不是头文件;是否像您必须超过的一定数量的行将文件视为源文件或什么?
  • @AmeenIzhac 以 .cpp 结尾的文件是您的源文件,以 .h 结尾的文件是您的头文件。长度无所谓。看看这个learncpp.com/cpp-tutorial/19-header-files (或任何其他解释头文件的教程,他们解释得比我以前都好)
【解决方案2】:

我曾经从一个答案中看到一个解释清楚的ascii图形插图,但现在找不到。

我发现了一些类似的图像。


基本上有3个阶段你需要知道,预处理阶段,编译阶段和链接阶段。我之前看过上面提供的答案,细节对初学者没有多大用处。

【讨论】:

  • 好的,谢谢你的图表,但正如我在问题中解释的那样,你有源文件 1,2 和 3 都是单独组装的,但我的理解是所有这些源文件都放在一个源中文件,然后将该源文件组装为一个
  • @AmeenIzhac No. 如果你有 3 个.cpp,那么你有 3 个对应的.o,在链接阶段之后,3 个.o 文件放入 1 个可执行文件中。不是你说的。没有人在.cpp 中包含另一个.cpp。只包含.h,而.h 中的大部分内容用于声明,这意味着让.cpp 中的代码知道您可以调用一些例如函数的东西。但实现在其他源文件中。
  • 哦,好的,谢谢,我不明白 .cpp 文件和 .h 文件之间存在差异。还有声明和实现有什么区别?
  • @AmeenIzhac 从编辑器的角度来看,它们只是具有不同后缀的文件。但是编译器对它们的处理方式不同。如果你连声明和实现的概念都不知道,我认为你需要阅读一些关于C++的基础知识,而不是在这里浪费你的时间,等待有人解释。你可以很容易地用谷歌搜索这些东西。
  • 我知道什么是声明变量和函数,我知道什么是实现,它基本上意味着编写一些东西,但实际上所有代码都是实现,所以即使是头文件 r 实现,所以我想知道你是否可以详细说明每种文件的内容
【解决方案3】:

在 C++ 中,我们将头文件(.h 或 .hpp)和代码文件(.cpp)分开 在头文件中你通常定义你的对象结构,在代码中你编写代码来实现它的逻辑。

在代码文件的顶部通常包含一组标题

#include <iostream>
#incluie <string>

预处理器将获取这些头文件中定义的类的定义,并创建一个包含所有定义的大文件。

代码文件本身将自行编译为单个 .o 文件

例如:

song.h

#include <string>

class Song {
   public:
      std::string getLyrics();
};

person.h

#include "song.h"  // Since person sing a Song

class Person {
   public:
      void sing(Song song);
};

所以在这种情况下,如果你在代码中这样做

main.cpp

#include "person.h"

int main() {
    Person person;
    Song song;

    person.sing(song);
}

预处理器会将您的 main.cpp 与标头组合成一个大文件。 这就是编译器会看到的。

class string { ...}

class Song {
   public:
      std::string getLyrics();
};

class Person {
   public:
      void sing(Song song);
};

int main() {
    Person person;
    Song song;

    person.sing(song);
}

现在,当您将实现添加到 .cpp 文件中时。 每个实现都将单独编译(歌曲、人物)。

Song.cpp

#include "song.h" // get the definition of Song

 std::string Song::getLyrics() {
    return "Every little thing, gonna be alright (bob Marley)\n";
}

Person.cpp

#include "person.h" // get the definition of Person

// Implementation
void Person::sing(Song song) {
    std::cout << song.getLyrics();
}

下一步是链接器,它在编译后的文件之间进行链接。

即main.o、person.o 和 song.o 之间的链接,因此 main 可以创建一个可以唱歌的人

【讨论】:

  • 所以所有代码都粘贴到一个文件中,但是编译器知道哪个代码来自哪个文件之间的区别,并将来自不同文件的每一段代码分别编译到不同的编译文件中,然后是链接器链接编译好的文件?
  • @AmeenIzhac 不完全是,编译器不知道哪个代码来自哪个文件。它并不在乎。它知道包含的头文件中所有必需的定义都在那里,因此代码可以使用它。在我的示例中,定义了 Song 和 Person,因此编译器知道结构并且 main 可以使用它们。
  • 好的,如果所有代码都在一个文件中,那么为什么要生成单独的目标文件?
  • @AmeenIzhac 因为它在重新编译整个东西时可以节省您的时间。例如,你有a.cppb.cpp,你得到a.ob.o 和一个exe,比如说a.exe。当你在b.cpp中编辑你的代码并重新编译时,你不必再次编译a.cpp,你可以再次使用a.o,只需再次编译b.cpp,得到新的b.o和新的a.exe。当您处理大型项目时,它可以节省时间。
  • 好的,谢谢_________
猜你喜欢
  • 2017-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-11
  • 2014-12-01
  • 2021-05-10
  • 1970-01-01
相关资源
最近更新 更多