为什么在 C 布尔宏中定义 TRUE (1 = = 1)而不是简单地定义为1?

我看过 C 的定义

#define TRUE (1==1)
#define FALSE (!TRUE)

这有必要吗? 简单地将 TRUE 定义为1,而将 FALSE 定义为0有什么好处?

27331 次浏览

如果编译器支持,这种方法将使用实际的 boolean类型(并解析为 truefalse)。(特别是 C + +)

但是,最好检查 C + + 是否正在使用(通过 __cplusplus宏) ,并实际使用 truefalse

在 C 编译器中,这相当于 01
(注意,由于操作顺序的不同,删除括号将中断该值)

实际上没有区别。0被评估为 false1被评估为 true。事实上,使用 布尔表达式布尔表达式(1 == 1)或 1来定义 true并没有什么不同。他们都被评估为 int

注意,C 标准库为定义布尔值提供了一个特定的头: stdbool.h

#define TRUE (1==1)
#define FALSE (!TRUE)

相当于

#define TRUE  1
#define FALSE 0

C 调。

关系运算符的结果是 011==1保证被评估为 1!(1==1)保证被评估为 0

绝对没有理由使用第一种形式。注意,第一种形式的效率并不低于几乎所有编译器,常量表达式是在编译时而不是在运行时计算的。根据这条规则,这是允许的:

(C99,6.6 p2)“常数表达式可以在翻译过程中而不是运行时求值,因此可以在常数可能存在的任何地方使用。”

如果你不对 TRUEFALSE宏使用文字,PC-Lint 甚至会发出一条消息(506,常量值布尔值) :

对于 C,TRUE应该定义为 1。然而,其他语言使用的数量不是1,所以一些程序员认为 !0是安全的。

同样在 C99中,布尔宏 truefalsestdbool.h定义直接使用文字:

#define true   1
#define false  0

答案是可移植性。TRUEFALSE的数值并不重要。的重要之处在于,像 if (1 < 2)这样的语句计算结果为 if (TRUE),而像 if (1 > 2)这样的语句计算结果为 if (FALSE)

当然,在 C 语言中,(1 < 2)的计算结果是 1,而 (1 > 2)的计算结果是 0,所以正如其他人所说的,就编译器而言,没有实际的区别。但是通过让编译器根据它自己的规则定义 TRUEFALSE,你就把它们的含义明确地告诉了程序员,并且保证了程序和其他库的一致性(假设其他库都遵循 C 标准... ... 你会大吃一惊的)。


一些历史
一些 BASIC 语言将 FALSE定义为 0,将 TRUE定义为 -1。像许多现代语言一样,它们 03任何非零值都是 TRUE,但它们 04布尔表达式是 -1。他们的 NOT操作是通过添加1并翻转符号来实现的,因为这样做效率很高。所以“ NOT x”变成了 -(x+1)。这样做的一个副作用是,像 5这样的值的计算结果是 TRUE,但是 00的计算结果是 01,也就是 TRUE!找到这种虫子并不好玩。

最佳实践
根据 事实上规则,零被解释为 FALSE,而 任何非零值被解释为 TRUE,您应该使用 永远不要将布尔表达式与 ABC1或 FALSE进行比较。例子:

if (thisValue == FALSE)  // Don't do this!
if (thatValue == TRUE)   // Or this!
if (otherValue != TRUE)  // Whatever you do, don't do this!

为什么?因为许多程序员使用将 int当作 bool的快捷方式。它们是不一样的,但编译器通常允许这样做。比如说,写作是完全合法的

if (strcmp(yourString, myString) == TRUE)  // Wrong!!!

这个 看起来是合法的,编译器会很高兴地接受它,但是它可能不会做您想要的事情。这是因为 strcmp()的返回值是

0如果 yourString == myString
yourString < myString < 0
如果 yourString > myString > 0

因此,上面的行只有在 yourString > myString

正确的做法是

// Valid, but still treats int as bool.
if (strcmp(yourString, myString))

或者

// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)

同样地:

if (someBoolValue == FALSE)     // Redundant.
if (!someBoolValue)             // Better.
return (x > 0) ? TRUE : FALSE;  // You're fired.
return (x > 0);                 // Simpler, clearer, correct.
if (ptr == NULL)                // Perfect: compares pointers.
if (!ptr)                       // Sleazy, but short and valid.
if (ptr == FALSE)               // Whatisthisidonteven.

您经常会在生产代码中发现一些“糟糕的示例”,许多有经验的程序员对此深信不疑: 它们可以工作,有些比它们短(学究地说?)正确的选择,和习语几乎是普遍公认的。但是考虑一下: “正确”的版本并不会降低效率,它们保证是可移植的,它们甚至会通过最严格的命令行,甚至新的程序员也会理解它们。

这不值得吗?

(1 == 1)技巧对于定义 TRUE非常有用,它对 C 来说是透明的,但是在 C + + 中提供了更好的类型。同样的代码可以被解释为 C 或 C + + ,如果你用一种叫做“ Clean C”的方言编写(它可以编译成 C 或 C + +) ,或者如果你编写的 API 头文件可以被 C 或 C + + 程序员使用。

在 C 语言翻译单元中,1 == 11具有完全相同的含义,而 1 == 00具有相同的含义。但是,在 C + + 翻译单元中,1 == 1的类型是 bool。因此,TRUE宏定义了这种方式,更好地集成到 C + + 中。

例如,如果函数 foointbool有重载,那么 foo(TRUE)将选择 bool重载。如果 TRUE只定义为 1,那么它在 C + + 中就不能很好地工作。foo(TRUE)需要 int过载。

当然,C99引入了 booltruefalse,这些可以用在 C99和 C 的头文件中。

然而:

  • 这种将 TRUEFALSE定义为 (0==0)(1==0)的做法早于 C99。
  • 仍然有很好的理由远离 C99和工作与 C90。

如果您在 C 和 C + + 混合的项目中工作,并且不想要 C99,那么可以定义小写的 truefalsebool

#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif

也就是说,0==0的技巧是(是?)一些程序员甚至在代码中使用,而这些代码从未打算以任何方式与 C + + 进行互操作。这并不能说明什么问题,这表明程序员对布尔函数在 C 语言中的工作方式存在误解。


如果 C + + 的解释不清楚,这里有一个测试程序:

#include <cstdio>


void foo(bool x)
{
std::puts("bool");
}


void foo(int x)
{
std::puts("int");
}


int main()
{
foo(1 == 1);
foo(1);
return 0;
}

输出:

bool
int

至于从注释中提出的重载 C + + 函数是如何与 C 和 C + + 混合编程相关的问题。这些只是说明了类型差异。当编译为 C + + 时,希望 true常量为 bool的一个有效理由是为了进行干净的诊断。在其最高警告级别,C + + 编译器可能会警告我们,如果我们传递一个整数作为 bool参数的转换。使用 Clean C 编写代码的一个原因不仅是我们的代码更具可移植性(因为它被 C + + 编译器理解,而不仅仅是 C 编译器) ,而且我们可以从 C + + 编译器的诊断意见中受益。

除了 C + + (已经提到过) ,静态分析工具还有另一个好处。编译器将消除任何低效性,但是静态分析器可以使用自己的抽象类型来区分比较结果和其他整数类型,因此它隐含地知道 TRUE 必须是比较的结果,不应该被假定为与整数兼容。

显然,C 表示它们是兼容的,但是您可以选择禁止故意使用该特性来帮助突出 bug ——例如,某些人可能混淆了 &&&,或者他们搞错了操作符的优先级。

  1. 列表项目

通常在 C 语言编程语言中,1被定义为 true,0被定义为 false。因此,为什么你经常看到以下内容:

#define TRUE 1
#define FALSE 0

不过,任何不等于0的数字在 If判断语句中也会被计算为真。因此,使用以下方法:

#define TRUE (1==1)
#define FALSE (!TRUE)

你可以明确地表明,你试图通过使 false 等于无论什么都不为真来保险起见。

我们不知道 TRUE 等于的确切值,编译器可以有自己的定义。因此,您需要使用编译器的内部定义。如果您有良好的编程习惯,但是可以避免一些糟糕的编码风格带来的问题,例如:

如果((a > b) = = TRUE)

如果您手动将 TRUE 定义为1,而 TRUE 的内部值是另一个值,那么这可能是一个灾难。