头文件在整个程序中仅包含一次
Header file included only once in entire program?
我知道这是一个常见的问题,但我仍然无法完全理解它。
在从多个不同的源文件和头文件生成的 C 或 C++ 程序中,当使用头保护时,每个头文件是否只包含一次到整个代码中?
以前有人告诉我,一个头文件(带有包含保护)只会在一个翻译单元中包含一次,但在整个代码中会包含多次。这是真的吗?
如果它在整个代码中只包含一次,当一个文件希望包含它并且预处理器检测到它已被包含时,希望使用它的文件如何知道它之前包含的代码中的下落?
这是过程:
source header source header header
/ | / /
/ | / /
PREPROCESSOR PREPROCESSOR
| |
V V
preprocessed code preprocessed code
| |
COMPILER COMPILER
| |
V V
object code object code
/
/
/
LINKER
|
V
executable
预处理
#include
是第一步。它指示预处理器处理指定的文件,并将结果插入到输出中。
如果A
包括B
和C
,并且B
包括C
,则预处理器的输出A
将包含两次C
的处理文本。
这是一个问题,因为它会导致重复声明。一种补救措施是使用预处理器变量跟踪源代码是否已包含在内(也称为标头保护)。
#ifndef EXAMPLE_H
#define EXAMPLE_H
// header contents
#endif
第一次,EXAMPLE_H
是未定义的,预处理器将评估ifndef
/endif
块内的内容。 第二次,它将跳过该块。因此,处理后的输出会更改,并且定义仅包含一次。
这非常普遍,以至于某些编译器实现了一个非标准指令,该指令更短,不需要选择唯一的预处理器变量:
#pragma once
// header contents
您可以确定您希望 C/C++ 代码的可移植性以及使用哪个标头保护。
标头保护将确保每个标头文件的内容最多出现在翻译单元的预处理代码中一次。
编译
编译器从预处理的 C/C++生成机器代码。
通常,头文件仅包含声明,而不包含实际定义(也称为实现)。编译器包括当前缺少定义的任何内容的符号表。
连接
链接器合并对象文件。它将定义(也称为实现)与对符号表的引用进行匹配。
可能是两个对象文件提供定义,链接器将采用一个。如果您已将可执行代码放在标头中,则会发生这种情况。这在 C 语言中通常不会发生,但在C++中经常发生,因为模板。
标头"code"(无论是声明还是定义)在所有对象文件中多次包含,但链接器将所有这些内容合并在一起,因此它在可执行文件中仅存在一次。 (我排除了多次存在的内联函数。
"头文件"实际上是在编译开始之前由预处理器插入的。只要把它想象成只是"取代"它的#include
指令。
守卫...
#ifndef MY_HEADER_H
#define MY_HEADER_H
// the "guarded" part of the header file is here...
#endif
。在替换后执行。因此,标头实际上可以多次包含,但文本的"受保护"部分仅由预处理器传递给编译器一次。
因此,如果标头中有任何代码生成定义,它们当然会包含在编译单元(也称为"模块")的目标文件中。如果在多个模块中#include
相同的标头,则这些标头将出现多次。
对于static
定义,这完全没有问题,因为这些定义在模块(也称为文件范围)之外是不可见的。对于程序全局定义,这是不同的,将导致"多个定义"错误。
注意:这主要适用于 C。对于C++,存在显着差异,因为类等增加了允许多个全局对象/时间的额外复杂性。
具有适当包含保护的头文件将仅包含一次,每个翻译单元将只包含一次。严格来说,它可以多次包含,但预处理器#ifndef
和#endif
之间的部分将在后续包含时跳过。如果操作正确,这应该是文件的全部(或大部分)。
翻译单元通常对应于"源文件",尽管一些晦涩的实现可能使用不同的定义。如果单独编译的源文件包含相同的标头,则预处理器无法知道另一个文件已经包含它,或者任何其他文件是同一项目的一部分。
请注意,当您将多个源文件(翻译单元)链接到单个二进制文件中时,如果标头不仅包含声明、模板、标记为 inline
的函数定义或静态变量定义,则可能会遇到多个定义的问题。为了避免这种情况,您应该在标头中声明函数,并在单独的源文件中定义它们,该文件与其他源文件链接在一起。
每个翻译单元将包含一次头文件,是的。 每个程序可以多次包含它,因为每个翻译单元在编译过程中都是单独处理的。 它们在链接过程中汇集在一起,形成一个完整的程序。
- 当我在结构中包含多个数组时,我的程序会跳过一堆代码
- 搜索字符串是否至少包含一次从 0 到 9 的所有数字的最有效方法
- 至少包含一次字符 X 的 S 的不同子字符串的数量
- C++包含一组对象的类
- 即使包含字符串C++程序的编译错误
- Asio在包含处理程序的类被破坏后调用处理程序
- 我想在C++项目中包含一种脚本语言.Lua vs Bison/Yacc
- 为什么我可以在多个包含const int的cpp文件中包含一个头文件,而不会出现编译器错误
- 接收2个数组并检查1在另一个数组中包含多少次的程序
- c++模板,并包含防护程序
- 编辑C++/程序集调试器的源代码以包含其他程序集信息
- 用C++创建一个自包含的程序
- 如何将提升库包含在C++程序中
- 编写一个程序,读取一串包含标点符号的字符,并写入被读取的内容,但删除了标点符号
- 为什么可以从QWidget创建一个对象而不将其包含在C++程序的开头
- 有没有办法将源代码格式化为每行只包含一条语句
- 双链接列表总是只包含一条记录
- 页眉应包含一次
- 头文件在整个程序中仅包含一次
- 自包含应用程序中的异常安全性