将 true 重新定义为 false 时的期望输出是多少,反之亦然


#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,这是值为 0int的名称。

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

编辑:

§2.11/1:"表 3 中显示的标识符保留用作关键字。我不会尝试重现表 3 的所有内容,但它包括假和真。不过,这是否是绝对禁止可能会受到一些质疑,因为同一句话补充说:"(也就是说,它们在第 7 阶段被无条件地视为关键字(",这表明可以通过这种方式重新定义关键字,因为所涉及的宏将在阶段 7 之前扩展。

但是,在本例中,您还包含了 <iostream> ,这带来了另一个规则 (§17.4.3.1.1(:"包含标头的翻译单元不得包含定义在该标头中声明或定义的名称的任何宏。这样的翻译单位也不得为词法上与关键字相同的名称定义宏。

这里的措辞强烈表明,如果翻译单元不包含任何标题,则可以像您所做的那样自由地重新定义关键字,但考虑到#include <iostream>的存在,毫无疑问您有未定义的行为。

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

最新更新