为什么g ++预处理器包含未使用的头文件

时间:2016-02-15 10:01:30

标签: c++ g++

我已经创建了两个头文件和一个这样的cpp文件。

cat.h

class Cat
{
};

dog.h

class Dog
{
};

的main.cpp

#include "cat.h"
#include "dog.h"

int main()
{
 Cat my_cat;
 return 0;
}

然后我运行g++ -E main.cpp >out.cpp命令并生成预处理输出。就像这样。

# 1 "main.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.cpp"
# 1 "cat.h" 1
class Cat
{
};
# 2 "main.cpp" 2
# 1 "dog.h" 1
class Dog
{
};
# 3 "main.cpp" 2

int main()
{
 Cat my_Cat;
 return 0;
}

看了这个之后,我想起了几个疑惑。

  1. 为什么它包含dog.h内容,即使它从未在main.cpp文件中使用
  2. 在通常的编译中,在哪里创建这个预处理的临时文件(在内存或磁盘中)
  3. 每个处理和编译的顺序是什么(首先是每个进程所有文件,然后开始编译或逐个完成)
  4. 实际编译器是否使用#作为注释的语法

4 个答案:

答案 0 :(得分:1)

让我从我所知道的回答:

  1. 因为该文件包含在#include指令中。就是这样,没有对真正使用的内容进行分析。这种分析通常在人员或其他工具进行SW重构时完成,而不是由预处理器完成。
  2. 取决于编译器及其选项。例如,您可以要求GCC使用管道而不是在文件系统上创建中间文件
  3. 预处理,合并,链接。预处理按文件的包含顺序完成。意味着所有包含的文件与源文件一起连接到一个大文件,直到包含树的末尾。所有这些都形成了翻译单元,后来由编译器编译。

答案 1 :(得分:1)

关于数字4,这不是注释,这是另一种名为line control directives的指令,并告诉编译器下一段代码来自哪个文件和行。

编译器&#34;正确&#34;只看到translation units(大致是一个包含所有包含的头文件的源文件,就像你创建的那样)所以它实际上并不知道有关头文件的任何信息。行指令是编译器知道头文件中是否存在错误以及头文件中的错误的方法。

答案 2 :(得分:1)

预处理器只对源文件进行文本替换。所以

#include "dog.h"

只是替换为dog.h

的内容

它不检查源文件的内容以确定是否正在使用dog.h中的声明。

预处理(临时)文件的位置取决于实现。甚至可能没有这样的文件 - 预处理的文本可能只是直接从预处理器传送到另一个执行编译的可执行文件。

该标准要求实现(编译器和构建链)的行为就像按顺序完成多个阶段一样。一个描述是http://en.cppreference.com/w/cpp/language/translation_phases。但是,实际执行这些阶段不需要特定的实现 - 只需要提供结果,如果有的话。

一般来说,编译器(在某种意义上只是将预处理代码作为输入,并在该链接上实现阶段7)不需要将#视为注释。但是,由于它是在预处理之后调用的,因此实现可以自由执行。但请注意,预处理器会解释以#开头的行,因此会拒绝不恰当地执行此操作的代码(例如,您尝试使用#作为注释标记)。

答案 3 :(得分:0)

预处理器不是编译器。它不理解C,C ++或任何其他语言。它所做的只是处理预处理器指令(#...)和一些其他任务,如连接字符串文字。当然不能删除死代码。死代码删除由编译器或链接器完成。