[英]Why include guards?
僅僅是因為您可能希望編譯器將該文件加載兩次。
請記住, #include
只是加載文件並將其內容放置在指令的位置。 該文件可能是頭文件,但也可能是有用且經常使用的源代碼。
大多數現代的編譯器#pragma once
執行了您希望它們執行的操作,就會對#pragma once
做出反應。 但是請記住,這是語言規范中未包含的編譯器擴展,通常最好堅持包含防護措施-您可以肯定,它可以在任何編譯器上使用,並且在任何情況下都適用。
為什么我的編譯器(GCC)無法檢測到兩次加載相同的代碼
它可以(或者,在處理方面,處理標頭包含的預處理器可以)。 您可以使用非標准但得到廣泛支持的擴展,而不是使用包含保護
#pragma once
指示此標頭僅應包含一次。
並具有明智的默認行為?
該語言默認情況下未指定此行為,這主要是因為該語言的歷史可以追溯到跟蹤包含的標頭可能會非常昂貴的時候,部分原因是有時您確實希望包含一個以上的標頭。 例如,無論是否定義NDEBUG
都可以重新包含標准<assert.h>
標頭,以更改assert
宏的行為。
因為在某些特殊情況下重新包含文件很有用。
人為的丑陋示例:假設您有一個#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
支持#pragma once
,這是一種實現與包含保護相同的效果的更好方法。 但是,很遺憾,這是非標准的。
為什么我的編譯器(GCC)無法檢測到它兩次加載相同的代碼並具有明智的默認行為?
因為不是由編譯器執行包含處理。 它由預處理器完成,該預處理器本質上是一個文本轉換引擎。 對於文本轉換引擎來說,如果在處理一段文本時多次出現相同的包含內容,那將是很有意義的。
讓我們先說一下:編譯器不處理#include
。 這使得編譯器無法對符號重新定義做出明智的決定。
其他語言將模塊作為語言的一部分來實現,並且在那些語言中,事物不作為文本替換處理,並且編譯器實際上具有有關導入語義的知識。
包括防護措施可防止符號重新定義,並多次包含相同文件。
編譯器需要此機制,因為出於明顯的原因,它不包括分析和確定要考慮的代碼版本的機制。 想想如果兩個不同的頭文件中的相同函數簽名僅返回類型不同,將會發生什么情況。
假設內容僅與多個標頭中包含的內容完全相同,則編譯器將需要額外的計算能力和內存來跟蹤已包含的代碼。
所以這將是容易出錯且效率低下的
為什么我的編譯器(GCC)無法檢測到它兩次加載相同的代碼並具有明智的默認行為?
因為那樣的話它將不是C編譯器。 指定語言是為了使#include
創建文本包含,並且執行與規范不同的操作會破壞有效代碼。
顯而易見的后續問題:“我們可以更改C標准嗎?” 仍然必須找到某種方法來避免對現有有效代碼的相同破壞。
編譯器可以合法地做的一件事是,當一個非空文件(在處理#ifdef
等之后)被多次包含而沒有任何有意使用該文件的指示時,發出警告 。 如果您有足夠的動力,也許您可以為自己喜歡的編譯器准備合適的補丁?
順便說一句,您將發現問題變得非常棘手,因為您必須提出一個很好的“相同代碼”定義。
即使編譯器決定這樣做,它也需要跟蹤大量文件,並且需要多次(如itwasntpete所評論),編譯器無法區分實際代碼和頭文件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.