#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;
}
正如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
,它是一个值为0的int
类型名称。试图重新定义保留字会导致未定义的行为。
编辑:
§2.11 / 1:“表3中显示的标识符被保留用于关键字。”我不会尝试再现整个表3,但它包括false和true。这可能存在一些问题,是否绝对禁止这样做,因为同一句话补充说:“(也就是说,在第7阶段它们被无条件地视为关键字)”,这表明可以通过这种方式重新定义关键字,因为所涉及的宏将在第7阶段之前/时展开。
但是,在这种情况下,您还包括了<iostream>
,这引入了另一个规则(§17.4.3.1.1):“包含头文件的翻译单元不得包含定义在该头文件中声明或定义的名称的任何宏。此类翻译单元也不得为词法上与关键字相同的名称定义宏。“
这里的措辞强烈暗示,如果翻译单元不包括任何头文件,那么就可以自由地重新定义关键字,就像您所做的那样,但是鉴于存在#include <iostream>
, 没有疑问,您会有未定义的行为。
一旦您有了未定义的行为,关于“为什么”发生任何事情就没有更多可说的了 - 在那一点上,标准非常明确,任何行为都是允许的。
true
定义为false
,反之亦然?找一只最近的鞋子,重复用力地打自己的头,直到你弄清楚问题所在。 - Cody Gray