为什么要包括警卫?

我正在学习C.

在这里定义的包含守护程序用于防止在编译时加载相同的代码两次。

为什么我的编译器(GCC)不能检测到它加载了相同的代码两次,并有一个合理的默认行为?

只是因为你可能希望编译器加载该文件两次。

请记住, #include只是加载一个文件,并将其内容放在指令的位置。 这个文件可能是一个头文件,但也可能是有用的和经常使用的一段源代码。

大多数现代编译器都会对#pragma once做出正确的反应。 请记住,这是一个不包括在语言规范中的编译器扩展,通常是一个好主意,坚持包括守卫 – 你可以肯定的是,它可以在任何编译器和任何情况下工作。

为什么我的编译器(GCC)不能检测到它加载了相同的代码两次

它可以(或者,迂回地处理头部包含的预处理器可以)。 您可以使用非标准但被广泛支持的扩展,而不是使用include guard

 #pragma once 

以表明这个标题只能包含一次。

并有一个合理的默认行为?

该语言默认情况下不会指定这种行为,主要是因为语言的历史可以追溯到追踪包含标题的时间过于昂贵,部分原因是因为有时候您确实想要多次包含标题。 例如,标准的<assert.h>头文件可以包含或不包含定义的NDEBUG以改变assertmacros的行为。

因为存在奇怪的边缘情况,重新包括文件是有用的。

作出丑陋的例子:假设你有一个像这样的#include文件mymin.h

 // mymin.h : ugly "pseudo-template" hack MINTYPE min(MINTYPE a, MINTYPE b) { return (a < b) ? a : b; } 

然后你可以做这样的事情:

 #define MINTYPE int #include "mymin.h" #define MINTYPE double #include "mymin.h" 

现在,对于不同的types,有两个min重载,并且是http://thedailywtf.com/的一个很好的候选。; 谁需要模板? 😉

请注意,许多现代预处理器支持#pragma once ,这是获得与包括守卫相同的效果更好的方法。 但是,不幸的是,这是非标准的。

为什么我的编译器(GCC)不能检测到它加载了相同的代码两次,并有一个合理的默认行为?

因为它不是编译器在做包含处理。 它是由预处理器完成的,它本质上是一个文本转换引擎。 而对于文本转换引擎,如果在处理一段文本时同样包含多次出现则可以做到完美的意义。

让我们沉沦片刻:编译器不处理#include s。 这就是编译器无法对符号重新定义做出明智决定的原因。

其他语言将模块作为语言的一部分来实现,在这些语言中,事物并不是作为文本replace来处理的,编译器实际上对导入语义有了一定的了解。

即使编译器决定这样做,它需要跟踪大量的文件,并且很多次(如itwasntpete所述),编译器无法区分实际代码和头文件。

包括警卫防止符号重新定义和多次包括相同的文件。

编译器需要这种机制,因为出于显而易见的原因,它不包括分析和决定要考虑的代码版本的机制。 想想如果在两个不同的头文件中只有返回types相同的函数签名是不同的,会发生什么情况。

假设内容是完全一样的,只有它包含在多个头文件中,编译器需要额外的计算能力和内存来跟踪它已经包含的代码。

所以这将是错误倾向和不利的