【问题标题】:Why preprocessed file of simple C++ source file contains thousands lines of code?为什么简单 C++ 源文件的预处理文件包含数千行代码?
【发布时间】:2020-08-12 15:38:36
【问题描述】:

我正在学习 C++。我对 C++ 编译过程有所了解,编译过程的第一步是预处理。我很好奇预处理后源文件会发生什么,所以我用 C++ 编写了几行代码。这是我的代码:

#include <iostream>

using std::cout;
using std::endl;

#define PI 3.1416
int main()
{
    cout << "Hello World\n";
    cout << "The value of PI is: " << PI << '\n'; 
}

不超过十行代码。

我使用这个编译器标志g++ -E main.cpp &gt; main.p,所以预处理文件是main.p。 预处理后的文件main.p包含28634行代码。

main.p 的前 15 行如下所示:

# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.cpp"
# 1 "/usr/include/c++/9/iostream" 1 3
# 36 "/usr/include/c++/9/iostream" 3
       
# 37 "/usr/include/c++/9/iostream" 3

# 1 "/usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h" 1 3
# 252 "/usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h" 3

# 252 "/usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h" 3

最后 10 行 main.p 看起来像:

using std::cout;
using std::endl;


int main()
{
    cout << "Hello World\n";
    cout << "The value of PI is: " << 3.1416 << '\n';
}

为什么main.p 包含这么多行代码?我的假设(如果我错了请纠正我)是用于构建iostream 库的所有代码都包含在main.p 文件中,如果是这样,那么main.cpp 的二进制或可执行文件不是不必要的大吗?

【问题讨论】:

  • 这就是#include 指令的工作原理。它读取文件名并将#include 替换为该文件的内容(递归)。 iostream 是一个巨大的标头,因此包含它对于编译时间和二进制大小来说是一个(很小但仍然)开销。
  • 是的,所有包含都递归复制粘贴到每个翻译单元。
  • 您的假设是正确的,正如reference 中所述。这与可执行文件的大小无关。
  • 编译器非常聪明——尤其是当您打开优化时。它将采用预处理的源文件,并且仅在最终二进制文件中包含实际需要的位。源大小不等于二进制大小。

标签: c++ compilation iostream preprocessor


【解决方案1】:

您的假设是正确的:iostream 标头的全部内容都包含在预处理器输出中。这会传递地发生:iostream 包含的每个标头也被包含在内,依此类推。这基本上就是#include 的意思!

但是,这并不意味着您的可执行文件会很大:

  • 标头通常只包含声明,不包含实现。声明不包含任何可执行代码。实际实现在共享库中。
  • 标准库中的许多标头仅包含模板,在实例化之前不会发出任何代码。
  • 标头中允许内联实现,但它们在调用站点中是内联的,因此只要您不调用它们,它们也不会被编译。

【讨论】:

  • 那么如果我在 C++ 程序的多个文件中多次包含iostream,那么源文件的所有这些预处理文件都包含来自iostream 标头的所有声明?如果是这样,那似乎是多余的,对吧?
  • 这就是为什么你在标题中写包含保护的原因之一:#ifndef IOSTREAM_INCLUDED#define IOSTREAM_INCLUDED ...实际内容...#endif。如果您对预处理器的愚蠢性感到震惊,请记住,自 1960 年代 C 语言的第一个版本以来,它基本上没有改变,并且被设计为在当时的计算机限制范围内工作。
  • 在您发表评论之前,我对标头保护一无所知(我是 C++ 的新手),经过一番谷歌搜索后,我想我对此有一个小小的想法。您不认为 C++ 20 中的 module 在这方面会有所帮助吗? (抱歉,如果知道 module 对于 C++ 的新手来说似乎太前卫了)。
  • @Yousuf:它更好;这就是添加module 的原因。
【解决方案2】:

#include 指令将包含文件的内容粘贴到最终文件中以进行编译。对于 C++,单个包含可以从包含的文件中拖出许多其他包含,从而使生成的文件膨胀。

这正是 C++ 包含工作的方式。可执行文件大小将包含支持您的功能所需的代码,并且无法绕过它。

【讨论】:

  • 不认为模块(C++ 20)在这方面会有帮助吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多