161

I've seen definitions in C

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

Is this necessary? What's the benefit over simply defining TRUE as 1, and FALSE as 0?

4

8 回答 8

157

如果编译器支持,这种方法将使用实际boolean类型(并解析为trueand )。false(具体来说,C++)

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

在 C 编译器中,这等价于0and 1
(请注意,由于操作顺序,删除括号会破坏它)

于 2013-06-09T13:24:51.347 回答
140

答案是便携性。TRUE和的数值FALSE并不重要。重要的像评估到这样的语句和像if (1 < 2)评估到这样if (TRUE)的语句。if (1 > 2)if (FALSE)

诚然,在 C 中,(1 < 2)评估为1(1 > 2)评估为0,正如其他人所说,就编译器而言,没有实际区别。但是通过让编译器定义TRUEFALSE根据自己的规则,您可以使它们的含义对程序员明确,并且您可以保证程序和任何其他库的一致性(假设其他库遵循 C 标准......你会吃惊)。


一些历史一些BASIC
定义FALSE0TRUE-1像许多现代语言一样,他们任何非零值解释为TRUE,但他们评估为真的布尔表达式为-1。他们的NOT操作是通过加 1 和翻转符号来实现的,因为这样做很有效。所以'NOT x'变成了-(x+1)。这样做的一个副作用是,像这样的值5计算为TRUE,但NOT 5计算为-6,这也是TRUE!发现这种错误并不好玩。

最佳实践
考虑到零被解释为和任何非零值被解释为的事实上的规则,你永远不应该将看起来像布尔的表达式与or进行比较。例子:FALSETRUETRUEFALSE

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

为什么?因为很多程序员都使用了把ints当bools的捷径。它们不一样,但编译器通常允许这样做。因此,例如,写是完全合法的

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

看起来是合法的,编译器会很乐意接受它,但它可能不会做你想要的。那是因为的返回值strcmp()

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

所以上面的行TRUE只在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.

您经常会在生产代码中发现其中一些“糟糕的示例”,许多有经验的程序员都对它们发誓:它们可以工作,有些比他们(迂腐?)正确的替代方案要短,而且这些习语几乎得到了普遍认可。但是考虑一下:“正确”的版本同样有效,它们保证可移植,甚至可以通过最严格的 linter,甚至新程序员也能理解它们。

这不值得吗?

于 2013-06-09T14:07:52.373 回答
52

(1 == 1)技巧对于以对 C 透明的方式进行定义很有用TRUE,但在 C++ 中提供了更好的类型。如果您使用称为“Clean C”的方言(编译为 C 或 C++)或编写可供 C 或 C++ 程序员使用的 API 头文件,则可以将相同的代码解释为 C 或 C++。

在 C 翻译单元中,与 ;1 == 1具有完全相同的含义1。和1 == 0具有相同的含义0。但是,在 C++ 翻译单元中,1 == 1类型为bool. 所以以TRUE这种方式定义的宏可以更好地集成到 C++ 中。

它如何更好地集成的一个示例是,例如,如果函数foo具有 forint和 for的重载boolfoo(TRUE)则将选择bool重载。如果TRUE只是定义为1,那么它在 C++ 中不会很好地工作。foo(TRUE)会想要int超载。

当然,C99 引入booltrue, 和false和这些可以用于与 C99 和 C 一起工作的头文件中。

然而:

  • TRUE这种将and定义FALSE(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在最高警告级别,如果我们将整数作为bool参数传递,C++ 编译器可能会警告我们转换。用 Clean C 编写的一个原因不仅是我们的代码更便携(因为它可以被 C++ 编译器理解,而不仅仅是 C 编译器),而且我们可以从 C++ 编译器的诊断意见中受益。

于 2013-06-09T15:54:33.747 回答
18
#define TRUE (1==1)
#define FALSE (!TRUE)

相当于

#define TRUE  1
#define FALSE 0

在 C.

关系运算符的结果是0or 11==1保证被评估为1并且!(1==1)保证被评估为0.

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

(C99, 6.6p2) “常量表达式可以在翻译期间而不是运行时进行计算,因此可以在常量可能存在的任何地方使用。”

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

对于 C,TRUE应定义为1。但是,其他语言使用 1 以外的数量,因此一些程序员认为这样!0做很安全。

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

#define true   1
#define false  0
于 2013-06-09T13:29:45.293 回答
12

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

显然 C 表示它们是兼容的,但您可以选择禁止故意使用该功能来帮助突出错误 - 例如,有人可能混淆了&and &&,或者他们搞砸了他们的运算符优先级。

于 2013-06-09T17:41:57.043 回答
4

实际的区别是没有的。0被评估为false并且1被评估为true。您使用布尔表达式( 1 == 1) 或1, 来定义这一事实true没有任何区别。他们都被评估为int.

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

于 2013-06-09T13:25:39.577 回答
3

我们不知道 TRUE 等于的确切值,编译器可以有自己的定义。所以你提供的是使用编译器的内部定义。如果您有良好的编程习惯,但这并不总是必要的,但可以避免一些不良编码风格的问题,例如:

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

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

于 2013-06-10T09:25:37.913 回答
2
  1. 项目清单

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

#define TRUE 1 
#define FALSE 0

但是,任何不等于 0 的数字在条件语句中也将被评估为真。因此,通过使用以下内容:

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

您可以通过使 false 等于任何不正确的内容来明确表明您试图安全行事。

于 2013-06-09T22:14:27.400 回答