包含保护,如定义here,用于防止在编译时加载相同的代码两次。
为什么我的编译器(GCC)无法检测到它正在加载相同的代码两次并且具有合理的默认行为?
答案 0 :(得分:18)
只是因为您可能希望编译器加载该文件两次。
请记住,#include
只是加载一个文件并将其内容放在指令的位置。此文件可能是一个头文件,但也可能是有用的,也经常使用一段源代码。
大多数现代编译器对#pragma once
的反应完全符合您的要求。但请记住,这是一个未包含在语言规范中的编译器扩展,并且通常坚持使用包含警卫是一个好主意 - 您必须确定它适用于每个编译器以及任何情况。
答案 1 :(得分:12)
为什么我的编译器(GCC)无法检测到它正在加载相同的代码两次
它可以(或者,迂腐地处理标头包含的预处理器)。您可以使用非标准但广泛支持的扩展
,而不是使用包含保护#pragma once
表示此标题只应包含一次。
并且有明智的默认行为?
默认情况下,语言没有指定此行为,主要是因为语言可追溯到跟踪包含的标题可能过于昂贵的时候,部分原因是有时您确实想要多次包含标题。例如,可以在定义<assert.h>
或不定义NDEBUG
的情况下重新标记标准assert
标头,以更改{{1}}宏的行为。
答案 2 :(得分:9)
因为存在重新包含文件的奇怪边缘情况。
拙劣的丑陋示例:假设您有#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"
现在,对于不同类型,您有两个min
重载,并且http://thedailywtf.com/是一个很好的候选者。谁需要模板? ;-)
请注意,许多现代预处理器都支持#pragma once
,这是实现与包含警卫相同效果的更好方法。然而,遗憾的是,它不合标准。
答案 3 :(得分:4)
为什么我的编译器(GCC)无法检测到它正在加载相同的代码两次并且具有合理的默认行为?
因为编译器不是在进行包含处理。它由预处理器完成,预处理器本质上是一个文本转换引擎。对于文本转换引擎,如果在处理一段文本时多次出现相同的包含,那么它就非常有意义。
让我们暂停一下:编译器不处理#include
s。这就是编译器无法对符号重新定义做出明智决策的原因。
其他语言将模块作为语言的一部分实现,在这些语言中,事物不会作为文本替换处理,编译器实际上也了解导入语义。
答案 4 :(得分:0)
包含防护措施以防止符号重新定义并多次包含相同的文件。
编译器需要这种机制,因为出于显而易见的原因,它不包括分析和决定考虑哪个代码版本的机制。想想如果两个不同的头文件中的相同函数签名只返回类型不同会发生什么。
假设内容完全相同,只有它包含在多个标头中,编译器需要额外的计算能力和内存来跟踪已经包含的代码。
因此它容易出错并且不合适
答案 5 :(得分:0)
为什么我的编译器(GCC)无法检测到它两次加载相同的代码并具有明智的默认行为?
因为那将不是C编译器。指定该语言是为了使<?php
$command = "ps -aef | grep elasticsearch | grep -v grep | grep '/usr/bin/java'";
exec($command, $pids);
if(count($pids) > 0){
print_r('elasticsearch service is running!');
}else{
$command = "service elasticsearch restart";
exec($command);
print_r('restarting elasticsearch service.');
}
print_r("\n");
创建一个文本转换,并且执行与规范不同的操作会破坏有效代码。
一个显而易见的后续问题:“我们可以更改C标准吗?”仍然必须找到某种方法来避免对现有有效代码的相同破坏。
编译器 可以合法地做的一件事是,当一个非空文件(在处理#include
等之后)被多次包含时,发出警告。没有任何迹象表明这是故意的。如果您有足够的动力,也许您可以为自己喜欢的编译器准备合适的补丁?
顺便说一句,您将发现问题变得非常棘手,因为您必须提出一个很好的“相同代码”定义。
答案 6 :(得分:-1)
即使编译器决定这样做,它也需要跟踪大量文件,并且多次(由itwasntpete评论),编译器无法区分实际代码和头文件。