#include <iostream>
#define true false
#define false true
int main() {
std::cout << false << true;
}
为什么输出&#34; 01&#34;?
答案 0 :(得分:13)
正如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
。
答案 1 :(得分:11)
任何重新定义保留字的尝试都会产生未定义的行为。
编辑:
§2.11/ 1:“表3中显示的标识符保留用作关键字。”我不会尝试重现表3中的所有内容,但它包括false和true。但是,由于同一句话增加了“(即,它们在第7阶段无条件地被视为关键词)”,这可能会对这是否是绝对禁止提出一些疑问“,这表明可以重新定义关键词因为所涉及的宏将在第7阶段之前扩展。
但是,在这种情况下,您还包括<iostream>
,这会带来另一条规则(第17.4.3.1.1节):“包含标题的翻译单元不应包含任何定义的宏在该标题中声明或定义的名称。此类翻译单元也不应为与关键字词汇相同的名称定义宏。“
这里的措辞强有力地表明,如果翻译单元不包含任何标题,则可以自由地重新定义关键字,但是考虑到{{1}的存在毫无疑问,你有未定义的行为。
一旦你有未定义的行为,就没有更多关于“为什么”发生任何事情的说法 - 在这一点上,标准非常明确,任何行为都是允许的。