奇怪的FALSE和TRUE的定义,为什么?

31

在我正在处理的某些代码中,我遇到了奇怪的真假重新定义。我以前见过这样的东西,是为了使检查更加严格/确切,但在我看来,这个有点奇怪,我想知道是否有人能告诉我这种定义的好处是什么,下面是我的注释:

#define FALSE (1 != 1) // why not just define it as "false" or "0"?
#define TRUE (!FALSE)  // why not just define it as "true" or "1"?

这个代码库中还有很多其他奇怪的异常情况。比如所有标准类型都有重新定义:

#define myUInt32 unsigned integer // why not just use uint32_t from stdint?

所有这些小“怪癖”让我觉得好像我错过了一些显而易见的东西,但实际上我真的看不到重点 :(
注意:严格来说这是C++代码,但它可能已从一个“C”项目移植过来。

4
有时候会使用这样的定义来获得源代码的可移植性,不仅限于不同平台上可能使用的不同C++编译器之间的可移植性,甚至还包括C编译器和C++编译器之间的可移植性。 - Kris Vandermotten
14
0 不同,如果编译器支持布尔类型,则 1!=1 具有正确的类型。 - Benjamin Bannier
6
你可能会错过至少以下一种组合:预先标准化的旧代码、“非我即敌”综合症和愚蠢。 - n. m.
1
我从这个问题中得到了一些非常好的反馈!非常感谢所有人给出的+1以及所有有用的答案 :) - code_fodder
1
@devnull 是的,它非常相似,我认为我错过了它,因为我的定义更偏向于“false”的一面:(,但还是......很好的答案,谢谢大家! - code_fodder
显示剩余4条评论
7个回答

35

意图似乎是可携带性

#define FALSE (1 != 1) // why not just define it as "false" or "0"?
#define TRUE (!FALSE)  // why not just define it as "true" or "1"?

在支持布尔类型的语言中(如C++),这些具有布尔类型,而对于不支持的语言(如C——即使是C99和C11,尽管它们已经引入了显式的布尔数据类型),它们仍提供有用的数值。

在可能的情况下使用布尔类型对于函数重载很有好处。

#define myUInt32 unsigned integer // why not just use uint32_t from stdint?

如果有 stdint 可用,那就没问题了。你可能认为这是理所当然的,但世界很大呀!这段代码认识到了这一点。

免责声明:个人而言,我坚持标准并简单地说明编译器需晚于1990年发布。但我们不知道该项目的基本要求是什么。

这段代码的作者没有在注释中解释这一点,这是最糟糕的事情。


11
即使在C11中,“!=”和“!”运算符的结果类型也是“int”,而不是布尔类型。 - interjay
@DavidHammen C11不是C++11。 - R. Martinho Fernandes
1
@R.MartinhoFernandes - 你说得对。我忘记了C和C++都发布了2011版本的标准。我删除了我的评论。然而,值得一提的是,在C++中,原始布尔运算符的结果确实是bool而不是int - David Hammen
注意:1979年至1998年C++不一定有bool类型。同意“编译器发布时间晚于1990年是先决条件”的说法。像if (!!x)这样的结构在那时很常见。 - chux - Reinstate Monica
TRWTF 是什么意思? - Brandin
@Brandin:“真正的WTF”。把它看作是“你应该关注的真正问题”。有点这个意思。 - Lightness Races in Orbit

11
#define FALSE (1 != 1) // why not just define it as "false" or "0"?

我认为这是因为表达式(1!=1)类型取决于语言对布尔值的支持 - 如果是C ++,则类型为bool,否则为int

另一方面,0在两种语言中始终是int,而false在C中不被识别。


7
严格来说,这是C++代码,但它可能已从“C”项目移植而来。
这涉及可移植性,但实际上远不止于此。它是对语言定义的巧妙利用,以便符合这些语言。
这些看起来荒谬的宏并不像乍一看那么荒谬,事实上它们非常巧妙,因为它们保证了在C和C++中TRUEFALSE具有正确的值(和类型)truefalse(即使编译器是一个没有这些关键字的C编译器,所以你不能轻易地写一些像#define TRUE true的东西)。
在C++代码中,这样的构造将是无用的,因为该语言定义了truefalse作为关键字。 然而,为了让C和C++无缝地在同一个代码库中运行,你需要“一些”可以同时适用于两者的东西(除非你想使用不同的代码风格)。
这些宏的定义方式证明了C++标准明确地模糊了truefalse实际上具有什么值。C++标准规定:
bool类型的值为true或false。 [...] 零值、空指针值或空成员指针值被转换为false;任何其他值被转换为true。 [...] bool类型的prvalue可以转换为int类型的prvalue,false变为0,true变为1。
注意它是如何说明存在两个特定的值及其名称以及它们相应的整数值如何转换的,但它没有说这些值是什么。你可能会倾向于认为这些值显然是01(并且恰巧你可能猜对了),但事实并非如此。实际上,故意不定义实际值。
这类似于指针(特别是空指针)的定义。请记住,零的整数字面量转换为空指针,空指针转换为false,比较相等......等等。
很多关于哪个转换到哪里等等的内容,但它没有在任何地方说一个空指针必须具有二进制表示为零(实际上,存在一些奇异的架构,其中这种情况并不成立!)。

1
这是一个起源多样的习语。在应用程序源代码分为Pascal、C和多个汇编器的环境中,我们使用了类似的方法来防止愚蠢的错误。这是一种自然的表达“真实性”的技术(借用Colbertism<g>),因为每种语言都有不同版本的TRUE和FALSE。 - Mike Housky

6

其中许多原因是历史原因,例如从C语言迁移而来的旧代码,非标准C++编译器生成的代码,交叉编译代码(可移植性),为了支持向后兼容性,遵循代码风格或者不良习惯。

有一些编译器没有像<cstdint>一样提供整数类型,例如uint32_t,或者它们没有<cstdbool>。一个好的程序员必须定义所有内容,并且大量使用预处理器,以使其程序在不同的编译器上定义良好。

今天,我们可以使用<cstdint>true/false<cstdbool>等,每个人都很高兴!


5
这个定义的好处在于避免将TRUE或FALSE隐式转换为整数。这非常有用,可以确保编译器不会选择错误的函数重载。

4

在C语言中没有本地的布尔类型,因此你无法严格使用“false”或“true”,除非在其他地方定义它们,但是如何定义它们呢?

同样的论点适用于myUInt32 - 最初C语言中没有uint32_t和stdint中的其他类型,因此这提供了一种确保获取正确大小整数的方法。如果您将其移植到另一种架构上,只需更改myUInt32的定义,使其等同于一个宽度为32位的无符号整数 - 可以是long或short。


1

有一个很好的解释说明了false和FALSE之间的区别。我认为这可能会帮助更好地理解,尽管大多数答案已经解释过了。 在这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接