#include <iostream>
#define true false
#define false true
int main() {
std::cout << false << true;
}
爲什麼輸出「01」?重新定義真假時的預期輸出是什麼,反之亦然?
#include <iostream>
#define true false
#define false true
int main() {
std::cout << false << true;
}
爲什麼輸出「01」?重新定義真假時的預期輸出是什麼,反之亦然?
正如Jerry Coffin所指出的,你不能用一個關鍵字的名稱來定義一個宏。
但是,我們可以考慮另一個類似的例子,它具有明確的行爲和相同的結果。試想一下:
int TRUE = 1;
int FALSE = 0;
#define TRUE FALSE
#define FALSE TRUE
std::cout << FALSE << TRUE;
當您使用FALSE
,它被確定爲宏FALSE
,並通過宏觀的替補名單,這是單令牌,TRUE
取代。那替換是重新掃描爲進一步的宏取代。
替換中的TRUE
然後被識別爲宏並被其替換列表取代,該替換列表是單個標記FALSE
。該替換再次被重新掃描。
如果我們繼續進行重新掃描和替換,最終會出現無限循環,因此C(和C++)預處理規範指出宏替換不會在替換列表中遞歸。
因爲在這最後的替換列表會導致遞歸更換FALSE
,宏替換停止,我們剩下的FALSE
,這是一個int
用的0
值的名稱。
很好的解釋!但是,只有一個問題,你是否給了他想法來編寫更多的代碼? – noMAD 2012-03-01 20:03:55
他沒有寫。 – 2012-03-01 20:09:05
@noMAD:不,通常不應該像這樣寫代碼,但這並不意味着我們不應該解釋爲什麼這個代碼的工作方式。理解你的編程語言如何工作很重要,即使在「沒有人使用」的語言的「愚蠢」角落。爲了記錄,我已經寫了完全這樣的代碼,在一個單元測試中,我正在做一段時間的C預處理器的實現(當然,我有奇怪的愛好)。 – 2012-03-02 22:17:42
任何重新定義保留字的嘗試都會導致未定義的行爲。
編輯:
§2.11/ 1: 「在表3中所示的標識符被保留用作關鍵字」我不會嘗試重現表3的全部內容,但它包含錯誤和真實。儘管這是一個絕對禁止的問題,但可能存在一些問題,因爲相同的句子增加了「(也就是說,它們在第7階段被無條件地視爲關鍵詞)」,這表明可以重新定義關鍵字因爲所涉及的宏將在第7階段之前展開。
但是,在這種情況下,您還包含<iostream>
,這會帶來另一個規則(第17.4.3.1.1節):「包含標題的單元不得包含任何定義在該標題中聲明或定義的名稱的宏,也不應該爲與詞典中的詞彙相同的名稱定義宏。
這裏的措辭強烈表明,如果翻譯單元沒有包含任何標題,它可以自由地重新定義一個關鍵字,但是由於存在#include <iostream>
,所以存在毫無疑問,你有未定義的行爲。
一旦你有不確定的行爲,就沒有更多的關於「爲什麼」發生任何事情的說法 - 在這一點上,標準非常清楚,任何行爲都是允許的。
認真嗎?你寫了那個代碼?要將'true'定義爲'false',反之亦然?找到最近的鞋子並反覆敲打頭部,直到找出問題所在。 – 2012-03-01 19:56:33
我沒有寫這段代碼(=我只是想了解 – hired777 2012-03-01 19:59:36
它可以用一個鮮爲人知的,通常模糊的(但在某些情況下非常相關的)宏觀擴張的事實來解釋,但是當你開始引用標準時,你最好還是短路並說明Jerry Coffin已經陳述了什麼,就ISO而言,編譯器可能會啓動一場機器人革命,並讓機器人用最近的鞋子擊敗作者,使@CodyGray變得開心。 – delnan 2012-03-01 19:59:38