重新定义true为false时的预期输出是什么,反之亦然?

hir*_*777 8 c++ language-lawyer

#include <iostream>
#define true false
#define false true
int main() {
    std::cout << false << true;
}
Run Code Online (Sandbox Code Playgroud)

为什么输出"01"?

Jam*_*lis 13

正如Jerry Coffin所说,你不能定义一个名称是关键字的宏.

但是,我们可以考虑另一个类似的例子,具有明确定义的行为和相同的结果.考虑:

int TRUE = 1;
int FALSE = 0;

#define TRUE FALSE
#define FALSE TRUE

std::cout << FALSE << TRUE;
Run Code Online (Sandbox Code Playgroud)

当您使用时FALSE,它被标识为宏FALSE,并被该宏的替换列表替换,该列表是单个令牌TRUE.然后重新扫描该替换以进一步替换宏.

然后TRUE将替换中的宏标识为宏,并替换为其替换列表,即单个令牌FALSE.该替换再次被重新扫描.

如果我们继续重新扫描和替换,我们最终会进入无限循环,因此C(和C++)预处理规范声明宏替换永远不会在替换列表中进行递归.

由于替换FALSE此最终替换列表将导致递归,因此宏替换将停止,我们将继续使用FALSE,这是int值为的a 的名称0.

  • @noMAD:不,通常不应该像这样编写代码,但这并不意味着我们不应该解释为什么这段代码的工作方式.重要的是要了解您的编程语言是如何工作的,即使是在"没人使用"的语言的"愚蠢"角落.为了记录,我编写了完全相同的代码,在单元测试中,我正在研究一段时间的C预处理器的实现(不可否认,我有奇怪的爱好). (5认同)

Jer*_*fin 11

任何重新定义保留字的尝试都会产生未定义的行为.

编辑:

§2.11/ 1:"表3中显示的标识符保留用作关键字." 我不会尝试重现表3中的所有内容,但它包括false和true.但是,由于同一句话增加了"(即,它们在第7阶段无条件地被视为关键词)",这可能会对这是否是绝对禁止提出一些疑问",这表明可以重新定义关键词因为所涉及的宏将在第7阶段之前扩展.

但是,在这种情况下,您还要包括<iostream>,这会带来另一条规则(第17.4.3.1.1节):"包含标题的翻译单元不应包含定义在该标题中声明或定义的名称的任何宏.这样的翻译单元也不应该为与关键字词汇相同的名称定义宏."

这里的措辞强烈暗示,如果翻译单元没有包含任何标题,则可以自由地重新定义关键字,但是如果存在#include <iostream>,则毫无疑问未定义的行为.

一旦你有未定义的行为,就没有更多关于"为什么"发生任何事情的说法 - 在这一点上,标准非常明确,任何行为都是允许的.

  • @Griwes鉴于"语言 - 律师"问题的数量,我们可以得到标准中完美属性引用的答案. (2认同)