5

如果同一头文件包含在 c++ 程序的多个源文件中,那么它如何影响编译(尤其是g++)?

编译器是否只加载一次头文件并为包含头文件的每个源文件编译它,或者头文件将为包含它的每个源文件单独加载。

4

3 回答 3

4

大多数头文件都有针对多个包含的特殊保护,例如

#ifndef MY_HEADER_H
#define MY_HEADER_H

// header body...

#endif MY_HEADER_H

如果没有这种保护,可能会多次包含标头,这可能会导致编译或链接错误。

编译器可能足够聪明,可以避免多次读取文件。但是,即使没有,操作系统也非常擅长缓存最近读取的文件,并且加载速度非常非常快——几乎是免费的。

于 2013-05-31T07:02:45.530 回答
4

对 g++: #includes 是由预处理器完成的,而不是编译器本身。-E您可以使用 g++ 的开关查看预处理的结果。(编辑:预处理器曾经是独立的,但现在是编译器可执行文件的一部分,但是为了回答这个问题,预处理阶段仍然是编译过程的一个不同阶段)。

使用 gcc、clang、icc 和 msvc,每个文件每次遇到时都会被访问,即使在同一个源文件中也是如此。

唯一不正确的情况是头文件包含#pragma once语句。一些编译器对包含守卫的用户进行了类似的优化:

#ifndef THIS_FILE_H
#define THIS_FILE_H 1
/* the stuff in thisfile.h */
#endif

msvc 和 gcc(可能还有 clang)支持一种称为“预编译头文件”的技术,可以帮助您避免使用一组常用头文件的编译头。

通常这是通过拥有一个包含所有#includes 的 .h 或 .cpp 文件来完成的;然后,您#include首先在每个文件中使用此文件(或使用“强制包含”的想法:msvc 中的 /Fi,gcc 中的 -include)。使用给定 pch 的每个文件都必须具有相同的定义和编译器选项。

如果您要编写以下 .h 文件

// bah.h
"bah",

和以下 .cpp 文件

#include <stdio.h>

const char* words[] = {
    "hello",
#include "bah.h"
    "world",
#include "bah.h"
#include "bah.h"
    NULL
};

int main(int argc, const char* argv[])
{
    for (size_t i = 0; words[i] != NULL; ++i ) {
        printf("%s\n", words[i]);
    }
    return 0;
}

输出将是

你好
呸
世界
呸
呸
于 2013-05-31T07:08:01.830 回答
1

处理器简单地替换每个源文件中的宏定义,完成后,编译器将开始将每个源文件编译成独立的汇编文件,然后由汇编程序翻译成二进制机器码。链接器最终会将所有对象文件链接到单个执行文件共享对象

因此,在预处理过程中,它基本上与编译器无关。而 g++ 是一组工具,包括预处理器、编译器、链接器。

于 2013-05-31T07:21:33.620 回答