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


160

我看过C中的定义

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

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


35
多:#define TRUE (’/’/’/’); #define FALSE (’-’-’-’)(摘自coding-guidelines.com/cbook/cbook1_1.pdf第871页)
osgx 2013年

2
不,这是笨蛋的妄想症。在C中,1和0在所有情况下都相同。
詹斯

@osgx是什么意思?
mrgloom

Answers:


155

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

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

在C编译器中,这等效于01
(请注意,删除括号会由于操作顺序而导致括号破损)


7
这是不正确的,此处不使用bool。的结果1==1int。(见stackoverflow.com/questions/7687403/...。)

4
@Mat:即使在C ++中,boolean类型也一样?
SLaks

9
问题被标记为C,但实际上在C ++中,关系运算符返回truefalse
2013年

5
@Mat:我猜想这样的代码是用C头编写的,以便与C ++很好地玩
SLaks

20
@SLaks如果想用C ++发挥好,它会#define TRUE true#define FALSE false随时__cplusplus被定义。
Nikos C.

137

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

诚然,在C,(1 < 2)计算结果为1(1 > 2)的计算结果为0,这样其他人所说,有尽可能的编译器来说没有实际的区别。但是通过让编译器根据自己的规则进行定义TRUE和定义FALSE,可以使程序员明白其含义,并且可以保证程序和任何其他库之间的一致性(假设其他库遵循C标准...吃惊)。


一些历史
一些基本定义FALSE0TRUE作为-1。与许多现代语言一样,他们任何非零值都解释TRUE,但是他们将布尔表达式的真实值评估-1。他们的NOT操作是通过加1并翻转符号来实现的,因为这样做的效率很高。因此,“ NOT X”变为-(x+1)。这样做的副作用是,类似的值5计算为TRUE,但NOT 5计算为-6,这也是TRUE!发现这种错误并不有趣。

最佳实践
鉴于事实上的规则,零被解释为FALSE任何非零值都被解释为TRUE,您绝对TRUEFALSE不应将布尔型表达式与进行比较。例子:

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()就是

      如果yourString == myString
    <0如果yourString < myString
    > 0则为0yourString > 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.

您通常会在生产代码中发现其中一些“不好的例子”,并且许多有经验的程序员都对它们发誓:它们可以工作,有些比其正确的替代方法还短,并且这些习语几乎是世界公认的。但是请考虑:“正确的”版本效率不低,可以保证它们是可移植的,它们甚至可以通过最严格的要求,甚至新的程序员也可以理解它们。

那不值得吗?


6
(1==1)没有比1。编译器自己的规则是C语言的规则,对于相等性和关系运算符的语义是清晰而明确的。我从未见过编译器弄错了这些东西。
基思·汤普森

1
实际上,strcmp已知by 所返回的值小于,等于或大于0。它不能保证为-1、0或1,并且在野外有些平台不返回这些值以加快实现速度。因此,如果不是这样,strcmp(a, b) == TRUE那么a > b反向含义可能就不成立了。
Maciej Piechotka

2
@KeithThompson-也许“可移植性”是错误的术语。但是事实仍然是(1 == 1)是布尔值;1不是。
亚当·里斯

2
@AdamLiss:在C中,(1==1)并且1都是int带有值1 的type常量表达式。它们在语义上是相同的。我想您可以编写代码来迎合那些不知道这些的读者,但是它的目的是什么?
Keith Thompson

2
实际上,“ not” 5在位级别为-6。
woliveirajr 2013年

51

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

在C翻译单位中,1 == 1与的含义完全相同1;与... 1 == 0具有相同的含义0。但是,在C ++转换单元中1 == 1具有type bool。因此,以TRUE这种方式定义的宏可以更好地集成到C ++中。

如何更好地集成的一个示例是,例如,如果函数foo具有for int和for 的重载boolfoo(TRUE)则将选择bool重载。如果TRUE仅将其定义为1,则在C ++中将无法正常工作。foo(TRUE)会想要int过载。

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

然而:

  • 这种做法定义的TRUE,并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 ++混合编程相关。这些仅说明了类型差异。希望将true常量bool编译为C ++时的一个合理原因是进行清晰的诊断。在最高警告级别,如果我们将整数作为bool参数传递,则C ++编译器可能会警告我们有关转换的信息。用Clean C编写代码的原因不仅是我们的代码更具可移植性(因为C ++编译器不仅理解C编译器可以理解它),而且我们可以从C ++编译器的诊断意见中受益。


3
优秀且未得到充分理解的答案。TRUE在C ++中,这两个定义完全不同并不十分明显。
user4815162342 2013年

4
重载函数与同时编译为C和C ++的代码有何关系?
基思·汤普森

@KeithThompson这不仅与重载有关,而且通常是正确的键入,重载只是发挥作用时最实用的示例。当然,没有重载的C ++代码,模板以及为“ C兼容性”而删除的所有“复杂”内容实际上根本不关心类型,但这并不意味着应该放弃给定语言的概念性类型限制。 。
Christian Rau

1
@ChristianRau:您的意思是“不是很在乎类型”吗?类型是C语言的中心。C程序中的每个表达式,值和对象都具有定义明确的类型。如果要在C和C ++中定义不同的内容(在极少数情况下,您实际上需要编写同时编译为C和C ++的代码),则可以用来#ifdef __cplusplus更清楚地表达您的意图。
Keith Thompson

@KeithThompson是的,我知道类型的重要性。这只是没有所有的感知类型的东西,如超载和模板之类的东西之间的区别bool,并int没有多大关系在实践中,因为它们是隐式转换为对方(在ç实际上“相同的”,注意引号,但是)在很多情况下您确实不需要对两者进行歧义“不多”可能太重了,“与使用模板和重载的代码相比要少得多”也许会更好。
Christian Rau

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

相当于

#define TRUE  1
#define FALSE 0

在C中

关系运算符的结果为011==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

1
我怀疑,每次使用TRUE时都将TRUE替换为1 == 1,而仅使用1会替换1,这不是第一种方法会产生额外的比较开销吗?或者它是经过优化的编译器?
pinkpanther

4
@pinkpanther常量表达式通常在编译时求值,因此不会引起任何开销。
哇,2013年

2
1==1保证被评估为1
ouah 2013年

3
@NikosC。这是一个很好的问题。这对于形式的代码很重要,这种形式的代码if(foo == true)将从错误的做法变为平淡的越野车。
djechlin 2013年

1
+1指出危险的(x == TRUE)真值与x
约书亚·泰勒

12

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

显然,C表示它们是兼容的,但是您可以选择禁止故意使用该功能来帮助突出显示错误-例如,在有人可能会感到困惑&和的地方&&,或者他们混淆了操作员的优先级。


1
很好,也许其中一些工具甚至可以if (boolean_var == TRUE) 通过扩展来捕获愚蠢的代码,if (boolean_var == (1 == 1))这要归功于(1 == 1)节点的增强的类型信息而成为模式if (<*> == <boolean_expr>)
卡兹(Kaz)2013年

4

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

请注意,C标准库提供了用于定义布尔值的特定标头:stdbool.h


当然您还没有...但是有些人可能会另外考虑,尤其是对于负数,这就是为什么:)
pinkpanther

什么?你有倒退。 true被评估为1并被false评估为0。C不了解本机布尔类型,它们只是整数。
djechlin 2013年

在C中,关系和相等运算符产生type的结果int,其值为01。C不用有一个实际的布尔类型(_Bool与宏bool定义<stdbool.h>,但只在C99加入,这并没有改变运营商的语义使用新的类型。
基思·汤普森

@djechlin:从1999年标准开始,C 确实具有本机布尔类型。称为_Bool,并<stdbool.h>具有#define bool _Bool
Keith Thompson

@KeithThompson,您的1 == 1评价为是正确的int。编辑。

3

我们不知道TRUE等于的确切值,并且编译器可以具有自己的定义。因此,您的私有权限是使用编译器的内部定义。如果您有良好的编程习惯,那么这并非总是必要的,但是可以避免某些不良编码风格的问题,例如:

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

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


在C语言中,>运算符始终将1表示为true,0表示false。任何C编译器都不可能出错。平等比较,以TRUEFALSE为风格差; 以上更清楚地写为if (a > b)。但是,不同的C编译器可以不同地对待真假的想法是错误的。
基思·汤普森

2
  1. 项目清单

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

#define TRUE 1 
#define FALSE 0

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

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

您可以通过使false等于不正确的值来明确表明您试图安全地工作。


4
我不会称其为“安全使用”,而是您给自己一种虚假的安全感。
dodgethesteamroller
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.