C 中 if(function() == TRUE) 的任何原因

Any reason for if(function() == TRUE) in C

问题:if(SomeFunction() == TRUE) 而不是做 if(SomeFunction()) 是否可以防止某种类型的编码错误?我试图了解这是否是为了防止某些隐藏的地雷,或者这是不是有人编写的代码不太了解如何计算表达式的结果。我知道如果做对了,这两件事的评价是一样的。就像 if(value == 42)if(42 == value) 的评估一样——仍然,有些人更喜欢第二个版本,因为如果有人打错了 == 并改为写 =,它会产生编译器错误。

背景: 我继承了一些 4 或 5 年前由不再在这里工作的人编写的嵌入式软件。我正在进行一些重构以摆脱数百行函数和全局变量以及所有爵士乐,所以这个东西是可读的,我们可以继续维护它。 pic 微处理器的代码是 c。这可能相关也可能不相关。代码中有各种奇怪的东西,尖叫 "didn't know what they were doing" 但这里有一个特定的模式(反模式?),我试图了解是否有充分的理由

模式: 这里有很多采用

形式的 if 语句
if(SomeFunction() == TRUE){
  . . .
}

其中 SomeFunction() 定义为

BOOLEAN SomeFunction(void){
  . . .
  if(value == 3)
    return(FALSE);
  else
    return(TRUE);
}

让我们忽略 if 语句主体中 SomeFunction returns TRUE 或 FALSE 的奇怪方式,以及它们使 'return' 看起来像函数调用的奇怪方式。

这似乎打破了 c 认为 'true' 和 'false' 的正常值,他们真的想确保返回的值等于定义为 TRUE 的任何值。这几乎就像他们正在制造三种状态 - TRUE、FALSE 和 'something else' 他们不希望在返回 'something else' 时采用 'if' 语句。

我的直觉是,这是一个奇怪的反模式,但我想让这些人从怀疑中获益。例如,我认识到 if(31 == variable) 看起来有点奇怪,但它是这样写的,所以如果您打错了 ==,您就不会意外地将 31 分配给变量。写这篇文章的人是为了防止类似的问题,还是这只是胡说八道。

附加信息

Is there a good reason for doing if(SomeFunction() == true) instead of doing if(SomeFunction())

没有

如果 SomeFunction() return 是类型 _Bool 的结果,那么相等比较应该是可靠的(假设结果的评估不涉及未定义的行为)。但是使用 TRUE 而不是 true,类型名称 BOOLEAN 而不是 _Boolbool,表明结果不是实际的 _Bool(仅在 C99 或更高版本中可用),但一些 ad-hoc Boolean-like 类型——可能是 int.

的别名

任何标量类型的值都可以用作 if 语句中的条件。如果该值等于零,则条件为假;否则,条件为真。如果 TRUE 定义为 1,并且 SomeFunction() returns,比如说 3,那么测试将失败。

写作

if (SomeFunction()) { /* ... */ }

更简单、更清晰并且更可能正确运行。

请注意,例如,在 <ctype.h> 中声明的 isdigit() 等函数不只是 return 01;如果参数是数字,isdigit() 可以(并且确实)return 任何 non-zero 值。使用它的代码应该能正确处理它——通过 not 比较它与 1trueTRUE 的相等性。

话虽如此,可能 是将某物与 TRUE 进行比较的正当理由——如果结果是否等于 [=21 很重要的话=] 或具有其他 non-zero 值。但在那种情况下,使用名称 BOOLEANTRUE 会产生误导。布尔类型的全部要点在于值要么为真要么为假。没有 "maybe",如果碰巧有不同的真理表示,你不应该关心你有哪一个。

我尝试遵循的准则是:

从不 将相等或不等的逻辑布尔值与 truefalse(或 01 进行比较, FALSE, TRUE).只需直接测试该值,如果要反转测试,请使用 ! 运算符。 ("logically Boolean" 值要么是 _Bool 类型,要么是为了区分真假而没有附加信息。如果 _Bool 不可用,后者可能是必需的。) false 可以是安全的,但没有理由这样做;直接对比数值还是比较清楚的

如果有人告诉你

if (SomeFunction() == true)

优于

if (SomeFunction())

问问他们为什么

if ((SomeFunction() == true) == true)

再好不过了。

另请参阅 comp.lang.c FAQ 的第 9 节。它对 C99 之前解决方案的强调可能有点过时,但它仍然有效。

更新:问题询问一个函数,该函数 return 是类型 BOOLEAN 的值 TRUE,定义如下:

typedef enum { FALSE, TRUE } BOOLEAN;

这样的定义在 1999 之前的 C 中很有用,但 C99 添加了预定义的布尔类型 _Bool 和 header <stdbool.h>,它定义了宏 boolfalse,以及 true。我当前的建议:使用 <stdbool.h> 除非非常担心您的代码可能需要与不支持它的实现一起使用。如果这是一个问题,您可以使用

typedef enum { false, true } bool;

typedef int bool;
#define false 0
#define true 1

(我更喜欢第一个。)这不是 100% 兼容 C99 定义,但如果您明智地使用它,它会正常工作。

因为在 C 中,任何非零值都被认为是真的,只有零值是假的,所以在任何情况下你都不应该与一个特定的 TRUE 宏定义进行比较。它不必要地具体。形式:

if( fn() )

是最简单的形式,但如果您确实更喜欢与特定值进行比较,则 FALSE 进行比较,因此:

if( fn() != FALSE )  // Safer than '== TRUE', but entirely unnecessary

这将适用于 FALSE 的所有合理定义,并且如果 fn() 不是 BOOLEAN。但它仍然完全没有必要。

就个人而言,为了更容易调试,我更喜欢:

 BOOLEAN x = fn() ;
 if( x )

除了能够在 进入或跳过条件块之前 在调试器中观察 return 值外,您还有机会命名 x 一些自我记录和特定于上下文的东西,函数名称可能没有反映出来。在维护中,您更有可能维护一个变量名,而不是更正一个注释(或多个注释)。此外 x 然后可以在其他地方使用,而不是多次调用 fn() (如果它有副作用或状态可能不会 return 相同的值)。

用户定义的布尔类型和值的另一个问题是定义可能始终不一致 - 特别是如果您使用第三方代码,其作者也认为使用相同的符号名称定义自己的代码是个好主意你的。如果名称不同(例如 BOOL、BOOLEAN 或 OS_BOOL),当您的代码与此第三方代码接口时,您必须决定在任何特定情况下应使用哪个布尔类型,以及名称TRUE 和 FALSE 可能会与重新定义警告或错误发生冲突。

更好的方法是更新代码以使用 stdbool.h 和 real 布尔类型 bool(内置 _Bool in C99) 只能有两个值 truefalse。如果 fn() 不是 bool 函数并且 return 是一个非零或一的整数,这仍然不能保护你,但是编译器有可能会发出类型不匹配警告。在和案例中重构遗留代码可以做的最好的事情之一是将警告级别设置为高并调查并修复 所有 警告(而不仅仅是通过自由铸造!)。 =27=]

可能已经很晚了,我对 PIC 不是 100% 确定,但在 AVR 中,一些寄存器可供应用程序使用,它们提供非常快的读写速度。

编译器先将布尔变量放在那里,然后再使用SRAM。为了阻止编译器这样做,通常使用 8 位变量而不是布尔值,以便编译器将可用寄存器用于应用程序中使用最多的变量。通过这样做,应用程序 运行 更快,但您不应该使用布尔值或仅在它们被大量使用时才使用。唯一的选择是使用 uint8_t/int8_t 或者您可以使用自制类型作为布尔值同时作为 uint8_t/int_t.

对于return部分,我在嵌入式系统中一直这样做。你 return 函数中的一个值,该值可以代表操作成功。其他值用于显示错误或用于调试。这样一个变量用于所有事情,并且由于您希望避免使用布尔值,因此您可以充分利用 8 位变量。

总的来说,我想在这里强调一点,嵌入式人员比软件工程师更关心速度和内存。我和这两个人一起工作过,嵌入式系统工程师生成了一个看起来很垃圾的代码,但它 运行 速度很快并且消耗的内存很少。代码看起来 bad/ugly 的原因之一是因为编译器不像软件工程师使用的典型编译器那么聪明,有时特殊的调整(可能看起来很愚蠢)会产生更高效的汇编代码。另一方面,软件工程师将更多时间花在可读性上,可能不太关心代码的效率,因为编译器会完成大部分工作。

如果老嵌入式工程师经验丰富 and/or 很聪明,那么他们做所有这些事情可能都是有充分理由的。特别是如果他们非常了解硬件。

我希望这对您正在阅读的其余代码有所帮助。