为什么要包括警卫?
我正在学习C.
在这里定义的包含守护程序用于防止在编译时加载相同的代码两次。
为什么我的编译器(GCC)不能检测到它加载了相同的代码两次,并有一个合理的默认行为?
只是因为你可能希望编译器加载该文件两次。
请记住, #include
只是加载一个文件,并将其内容放在指令的位置。 这个文件可能是一个头文件,但也可能是有用的和经常使用的一段源代码。
大多数现代编译器都会对#pragma once
做出正确的反应。 请记住,这是一个不包括在语言规范中的编译器扩展,通常是一个好主意,坚持包括守卫 – 你可以肯定的是,它可以在任何编译器和任何情况下工作。
为什么我的编译器(GCC)不能检测到它加载了相同的代码两次
它可以(或者,迂回地处理头部包含的预处理器可以)。 您可以使用非标准但被广泛支持的扩展,而不是使用include guard
#pragma once
以表明这个标题只能包含一次。
并有一个合理的默认行为?
该语言默认情况下不会指定这种行为,主要是因为语言的历史可以追溯到追踪包含标题的时间过于昂贵,部分原因是因为有时候您确实想要多次包含标题。 例如,标准的<assert.h>
头文件可以包含或不包含定义的NDEBUG
以改变assert
macros的行为。
因为存在奇怪的边缘情况,重新包括文件是有用的。
作出丑陋的例子:假设你有一个像这样的#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相同的函数签名是不同的,会发生什么情况。
假设内容是完全一样的,只有它包含在多个头文件中,编译器需要额外的计算能力和内存来跟踪它已经包含的代码。
所以这将是错误倾向和不利的