我现在正在做一个大项目,维护所有这些包括警卫的东西让我抓狂!手写是令人沮丧的浪费时间.尽管许多编辑器都可以生成include-guards,但这并没有多大帮助:

  1. 编辑器根据文件名生成保护符号.当不同目录中有相同文件名的标题时,就会出现问题.他们两个都会得到同样的后卫.将目录 struct 包含到保护符号中需要编辑器采用一些奇特的方法,因为宏中的斜杠和反斜杠并不是最好的方法.

  2. 当我必须重命名一个文件时,我也应该重命名所有的include警卫(在ifndef中,定义,最好是endif的注释).真烦人.

  3. 预处理器充斥着成吨的符号,却不知道它们是什么意思.

  4. 尽管定义只包含一次,编译器仍然会在每次遇到头包含时打开头.

  5. 包括不适合名称空间或模板保护.事实上,它们正在颠覆名称空间!

  6. 你的守卫符号可能不会是唯一的.

当程序在单个目录中包含少于1000个标题时,它们可能是可接受的解决方案.但是现在呢?它很古老,与现代编码习惯无关.最让我困扰的是,这个问题几乎可以通过#pragma once指令完全解决.为什么它不是一个标准?

推荐答案

以一种完全可移植的方式定义像#pragma once这样的指令并不是微不足道的,因为它有明确的好处.它提出问题的一些概念并不是在所有支持C的系统上都定义得很好,以简单的方式定义它可能不会比传统的Include Guard带来什么好处.

当编译遇到#pragma once时,它应该如何识别该文件,以便不再包含其内容?

显而易见的答案是文件在系统上的唯一位置.如果系统对所有文件都有唯一的位置,但许多系统提供的链接(符号链接和硬链接)意味着"文件"没有唯一的位置,则这是可以接受的.仅仅因为文件是通过不同的名称找到的,就应该重新包含该文件吗?可能不会.

但现在有一个问题,如何定义#pragma once的行为,使其在所有平台上都有确切的含义——即使是那些没有目录的平台,更不用说符号链接了——并且在有目录的系统上仍能获得理想的行为呢?

可以说,文件标识是由其内容决定的,因此,如果包含的文件有#pragma once个,而包含的文件有exactly个相同的内容,那么第二个和随后的#include个将不起作用.

这很容易定义,并且具有定义良好的语义.它还具有良好的属性,因此,如果项目从支持和使用文件系统链接的系统移到不支持和使用文件系统链接的系统,它的行为仍然相同.

缺点是,每次遇到包含#pragma once的include文件时,都必须使用到目前为止已经包含的#pragma once对照其他文件判断其内容.这意味着在任何情况下都会受到与使用#include个防护装置类似的性能影响,并且会给编译器编写人员带来不小的负担.显然,这可以缓存结果,但对于传统的include-guard也是如此.

传统的include保护迫使程序员 Select 一个宏,该宏是include文件的唯一标识符,但至少其行为定义良好且易于实现.

考虑到潜在的trap 和成本,以及传统的包括警卫的工作的事实,标准委员会不觉得有必要标准化#pragma once对我来说并不令人惊讶.

C++相关问答推荐

为什么listen()(在调用accept()之前)足以让应用程序完成3次握手?

C语言中字符数组声明中的标准

我编译了一个新的c程序,并收到以下错误

显式地将值转换为它从函数返回的类型的含义是什么?

核心转储文件中出现奇怪的大小变化

在没有动态内存分配的情况下,用C语言最快地将各种数组复制到单个较大的数组中

如何跨平台处理UTF-16字符串?

非正规化边缘毛刺

插座打开MacOS组件

如何使用_newindex数组我总是得到错误的参数

我的C函数起作用了,但我不确定为什么

在txt文件中找到指定的字符串,并从数字中减go 相同的值

使用TCL C API导航到列表中的元素

C语言中MPI发送接收字符串时出现的分段错误

使用ld将目标文件链接到C标准库

通过k&;r语法的c声明无效

正在try 理解C++中的`正在释放的指针未被分配‘错误

如何在Rust中处理C的longjmp情况?

将数组中的所有元素初始化为 struct 中的相同值

无法在 C 中打开文本文件,我想从中读取文本作为数据并将其写入数组