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

158 c precompiler

我见过C中的定义

#define TRUE (1==1)
#define FALSE (!TRUE)
Run Code Online (Sandbox Code Playgroud)

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

SLa*_*aks 153

如果编译器支持,此方法将使用实际boolean类型(并解析为truefalse).(具体来说,C++)

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

在C编译器中,这相当于01.
(请注意,删除括号会因操作顺序而中断)

  • @SLaks如果它想用C++玩得很好,只要定义了`__cplusplus`,它就会`#define TRUE true`和`#define FALSE false`. (20认同)
  • 问题标记为C,但实际上在C++中,关系运算符返回"true"或"false". (9认同)
  • 这是不正确的,这里没有使用bool.`1 == 1`的结果是`int`.(请参阅http://stackoverflow.com/questions/7687403/is-the-true-result-of-or-defined.) (7认同)
  • @Mat:我猜这样的代码是用C头编写的,以便与C++一起使用 (5认同)
  • @Mat:即使在C++中,使用`boolean`类型? (4认同)
  • @NikosC.但是你没有写出标题 - 也许是谁认为他是聪明的. (2认同)
  • @IsaacRabinovitch:我们不得不猜测编写此代码的人的意图. (2认同)

Ada*_*iss 136

答案是便携性.的数值TRUEFALSE并不重要.什么重要的是,像声明if (1 < 2)的计算结果为if (TRUE)又像声明if (1 > 2)评估为if (FALSE).

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


一些历史
一些基本定义FALSE0TRUE作为-1.像许多现代语言一样,它们任何非零值解释TRUE,但它们评估的布尔表达式为真-1.他们的NOT操作是通过添加1并翻转符号来实现的,因为以这种方式执行它是有效的.所以'NOT x'成了-(x+1).这样做的一个副作用就是5评估值TRUE,但NOT 5评估为-6,这也是TRUE!找到这种bug并不好玩.

最佳实践
鉴于事实上的规则将零解释为FALSE并且任何非零值被解释为TRUE,您永远不TRUEFALSE应该将具有布尔外观的表达式与or 进行比较.例子:

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

为什么?因为许多程序员使用将ints视为bools 的快捷方式.它们不一样,但编译器通常允许它.因此,例如,编写它是完全合法的

if (strcmp(yourString, myString) == TRUE)  // Wrong!!!
Run Code Online (Sandbox Code Playgroud)

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

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

所以上面的行TRUE只返回yourString > myString.

正确的方法是这样做

// Valid, but still treats int as bool.
if (strcmp(yourString, myString))
Run Code Online (Sandbox Code Playgroud)

要么

// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)
Run Code Online (Sandbox Code Playgroud)

同理:

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.
Run Code Online (Sandbox Code Playgroud)

你会经常在生产代码中找到一些这些"不好的例子",许多有经验的程序员都会发誓:他们工作,有些比他们(迂腐?)正确的选择更短,而成语几乎被普遍认可.但要考虑:"正确"的版本效率并不低,它们保证可移植,即使是最严格的连接也会通过,甚至新程序员也会理解它们.

这不值得吗?

  • `(1 == 1)`不比'1'便携.编译器自己的规则是C语言的规则,它对于相等和关系运算符的语义是清楚明确的.我从来没有见过编译器弄错了这个东西. (6认同)
  • @KeithThompson - 也许"可移植性"是错误的术语.但事实仍然是(1 == 1)是一个布尔值; 1不是. (2认同)
  • @AdamLiss:在C中,`(1 == 1)`和`1`都是`int`类型的常量表达式,值为1.它们在语义上是相同的.我想你可以编写适合那些不知道的读者的代码,但它会在哪里结束? (2认同)
  • 在位级别,'not' 5 实际上是 -6。 (2认同)

Kaz*_*Kaz 50

这个(1 == 1)技巧对于以TRUE对C透明的方式定义很有用,但在C++中提供了更好的输入.如果您使用名为"Clean C"(可编译为C或C++)的方言编写,或者您正在编写可供C或C++程序员使用的API头文件,则可以将相同的代码解释为C或C++.

在C翻译单元中,1 == 1具有完全相同的含义1; 并且1 == 0具有相同的含义0.但是,在C++翻译单元中,1 == 1有类型bool.因此TRUE宏定义的方式更好地集成到C++中.

它如何更好地集成的一个例子是,例如,如果函数foo有for int和for 的重载bool,那么foo(TRUE)将选择bool重载.如果TRUE只是定义为1,那么它在C++中将无法正常工作.foo(TRUE)会想要int超载.

当然,C99引入bool,truefalse与这些可以在与C99和与C.工作头文件中使用

然而:

  • 定义的这种做法TRUE,并FALSE(0==0)(1==0)早C99.
  • 仍然有充分的理由远离C99并与C90合作.

如果您正在使用混合C和C++项目,并且不想要C99,请定义小写true,falsebool不是.

#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif
Run Code Online (Sandbox Code Playgroud)

话虽如此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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

bool
int
Run Code Online (Sandbox Code Playgroud)

关于如何重载C++函数与混合C和C++编程相关的注释中的问题.这些仅仅说明了类型差异.在编译为C++时想要一个true常量的正当理由bool是用于清洁诊断.在最高警告级别,如果我们将整数作为bool参数传递,C++编译器可能会警告我们转换.在Clean C中编写代码的一个原因不仅在于我们的代码更具可移植性(因为C++编译器,而不仅仅是C编译器可以理解它),但我们可以从C++编译器的诊断意见中受益.

  • 重载函数如何与编译为C和C++的代码相关? (4认同)
  • 优秀,低估,回答.在C++下,"TRUE"的两个定义将完全不同. (3认同)

oua*_*uah 18

#define TRUE (1==1)
#define FALSE (!TRUE)
Run Code Online (Sandbox Code Playgroud)

相当于

#define TRUE  1
#define FALSE 0
Run Code Online (Sandbox Code Playgroud)

在C.

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

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

(C99,6.6p2)"可以在转换期间而不是运行时评估常量表达式,因此可以在常量可能的任何位置使用."

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

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

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

#define true   1
#define false  0
Run Code Online (Sandbox Code Playgroud)

  • @pinkpanther常量表达式通常在编译时进行求值,因此不会产生任何开销. (4认同)
  • @NikosC.这是一个很好的问题.这对于`if(foo == true)`形式的代码很重要,它将从简单的不良实践转变为平坦的错误. (3认同)
  • 保证`1 == 1`被评估为'1` (2认同)

sh1*_*sh1 12

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

显然ç说,他们是兼容的,但你可以选择禁止故意使用该功能的帮助重头戏错误-例如,当某人可能有混淆&&&,或者他们已经贻误了运算符优先级.