【问题标题】:same header file in multiple source files in a c++ programc++程序中多个源文件中的相同头文件
【发布时间】:2013-05-31 06:42:33
【问题描述】:

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

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

【问题讨论】:

  • 如何加载头文件,AFAIK,未在 C++ 编译模型中指定。实现可以选择加载一次,按需加载(针对每个源文件),或者以我有限的大脑无法想象的其他奇怪方式加载它。
  • 基本上#include *.h 表示“替换”此处 *.h 文件中的所有文本
  • 从概念上讲,您编译的每个文件都会重新审视标题。实际上,您的编译器很有可能支持某种形式的预编译头文件,因此它将源代码编译为某种中间形式,然后在编译包含相同头文件的其他文件时使用它。
  • g++ 编译器是否有可能被优化为即使从不同的源文件多次调用也只加载一次头文件?请确认。
  • @birajbora 绝对可以。预编译的头文件很常见,不必在单个进程中存储在内存中。它们可以很容易地写入磁盘并供后续进程使用。

标签: c++ header g++


【解决方案1】:

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

#ifndef MY_HEADER_H
#define MY_HEADER_H

// header body...

#endif MY_HEADER_H

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

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

【讨论】:

  • 如果您阅读了这个问题,它是关于在多个 SOURCE 文件中包含标题。单个源文件中没有多个包含。我很确定您没有按要求回答问题,而是回答了一个完全不同的类似问题。
  • @mvp :是的,与第一次编译代码相比,重新编译速度非常快。
【解决方案2】:

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

使用 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;
}

输出将是

你好 呸 世界 呸 呸

【讨论】:

  • “对于g++:#include-s 是由预处理器完成的”并不完全正确:多年来,g++ 开始于cc1plus,这是一个单个程序进行预处理、解析、优化、代码排放......预处理由编译器 (cc1plus) 完成,而不是单独完成。 (这是 1990 年代 GCC 中的一个单独过程)。
  • 据我所知,在开始编译阶段之前,预处理器仍然是一个完整的离散过程?
  • 这在很久以前是正确的......今天是错误的。 cc1plus(来自 GCC 4.x)正在链接一个内部库 libcpp,它进行预处理和一些标记化。并且预处理过程(就在解析之前)增量发生...... GCC 不能通过一次预处理,然后解析预处理的表单(即使它似乎以这种方式工作)来工作。
【解决方案3】:

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

因此,在预处理过程中基本上不属于编译器的业务。而g++是一个工具包,包括预处理器、编译器、链接器。

【讨论】:

  • 如果编译器包会将每个源文件的相同头文件翻译成编译后的代码,那么代码优化发生在哪里?
  • @birajbora 在编译步骤中。我认为在优化头文件方面没有太多工作要做。毕竟,所有这些都是函数的声明或类的定义或其他需要统一表达的信息。您已在头文件中声明了所有函数,并在另一个 cc 文件中进行了实现。您不想在头文件中实现您的功能,是吗?但是当这种情况发生时,我认为它们会在编译步骤中独立进行优化,但我不确定。
猜你喜欢
  • 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
相关资源
最近更新 更多