C

时间:2016-04-06 18:53:18

标签: c embedded microchip mplab

问题: 执行if(SomeFunction() == TRUE)而不是执行if(SomeFunction())是否可以防止出现某种类型的编码错误?我试图了解这是否可以防止某些隐藏的地雷,或者是否是某人编写代码并且不太明白如何评估表达式的结果。我明白,如果做得对,这两件事情的评价都是一样的。就像if(value == 42)if(42 == value)评估相同 - 仍然,有些人更喜欢第二个版本,因为如果有人输错了==并且写了=而产生编译器错误。

背景 我继承了一些嵌入式软件,这些软件是4或5年前由不在这里工作的人写的。我正在进行一些重构,以摆脱数百行函数和全局变量以及所有爵士乐,所以这个东西是可读的,我们可以继续保持它。代码是c用于pic微处理器。这可能相关也可能不相关。代码中有各种奇怪的东西,尖叫着“不知道他们在做什么”,但是这里有一个特定的模式(反模式?),我试图理解是否有充分的理由

模式: 这里有很多if语句采用

形式
if(SomeFunction() == TRUE){
  . . .
}

其中SomeFunction()定义为

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

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

这似乎打破了c认为'true'和'false'的正常值。就像,他们真的想确保返回的值等于任何被定义为TRUE的值。这几乎就像他们正在制作三种状态 - TRUE,FALSE和'其他'而且他们不希望在返回“其他东西”的情况下采用'if'语句。

我的直觉是,这是一种奇怪的反模式,但我想给这些家伙带来疑问。例如,我认识到if(31 == variable)看起来有点奇怪,但它是这样编写的,所以如果你输错了==你不会意外地将31分配给变量。是那些写这篇文章的人可以防范类似的问题,或者这只是胡说八道。

其他信息

  • 当我写这个问题时,我的印象是stdbool不可用,但我现在看到它是由IDE提供的,只是没有在这个项目中使用。这使我更倾向于“没有充分理由这样做。”
  • 看起来BOOLEAN定义为typedef enum _BOOLEAN { FALSE = 0, TRUE } BOOLEAN;
  • 此处讨论的开发环境是MPLAB 8.6

3 个答案:

答案 0 :(得分:17)

  

是否有充分理由做if(SomeFunction() == true)而不是if(SomeFunction())

没有

如果SomeFunction()返回类型_Bool的结果,则相等性比较应该是可靠的(假设结果的评估不涉及未定义的行为)。但是使用TRUE而不是true,类型名称BOOLEAN而不是_Boolbool,表明结果不是实际的{{ 1}}(仅在C99或更高版本中可用),但是一些特殊的布尔类型 - 可能是_Bool的别名。

任何标量类型的值都可以用作int语句中的条件。如果该值等于零,则条件为假;否则,条件为真。如果if定义为TRUE,而1返回,例如SomeFunction(),则测试将失败。

书写

3

更简单,更清晰,更有可能表现得正确。

请注意,例如,if (SomeFunction()) { /* ... */ } 中声明的isdigit()等函数不仅仅返回<ctype.h>0;如果参数是一个数字,1可以(并且确实)返回任何非零值。使用它的代码应该正确处理它 - 通过而不是将它与isdigit()1true进行比较。

话虽如此,可能是将某些内容与TRUE进行比较的正当理由 - 如果结果等于TRUE或者是否有一些重要其他非零值。但在这种情况下,使用名称TRUEBOOLEAN会产生误导。布尔类型的重点是值为true或false;没有“可能”,如果碰巧有不同的真理表示,你就不应该关心你拥有哪一个。

我尝试遵循的准则是:

从不将相等或不等式的逻辑布尔值与TRUEtrue(或false0,{{1}进行比较},1)。如果要反转测试,只需使用FALSE运算符直接测试该值。 (“逻辑布尔”值可以是TRUE类型,也可以用于区分真假,而无需额外信息。如果!不可用,则后者可能是必要的。)与{的比较{1}}可以安全,但没有理由这样做;直接比较这个值还是比较清楚。

如果有人告诉你

_Bool

优于

_Bool

问问他们为什么

false

甚至没有更好。

另见comp.lang.c FAQ的第9节。它对C99前解决方案的重视可能有点过时了,但它仍然有效。

更新:该问题询问一个返回类型if (SomeFunction() == true) 的值if (SomeFunction()) 的函数,定义如下:

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

此类定义在1999年之前的C中很有用,但C99添加了预定义的布尔类型TRUE和标题BOOLEAN,它定义了宏typedef enum { FALSE, TRUE } BOOLEAN; _Bool<stdbool.h>。我目前的建议是:使用bool除非严重担心您的代码可能需要与不支持它的实现一起使用。如果这是一个问题,你可以使用

false

true

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

答案 1 :(得分:2)

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

if( fn() )

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

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

适用于FALSE的所有合理定义,以及fn()不是BOOLEAN的情况。但它完全没必要。

个人为了更容易调试我更喜欢:

 BOOLEAN x = fn() ;
 if( x )

除了能够在输入或跳过条件块之前观察调试器中的返回值之外,您还有机会将x命名为自我记录并且特定于上下文,函数名称可能不反映。在维护中,您更可能维护变量名称而不是更正注释(或许多注释)。此外,x可用于其他地方而不是多次调用fn()(如果它有副作用或状态可能不会返回相同的值)。

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

更好的方法是更新代码以使用stdbool.h和 real 布尔类型bool(C99中内置_Bool的别名)只能有两个值truefalse。这仍然不能保护您免受fn()不是bool函数并且返回除零或一之外的整数的情况,但是编译器有可能发出类型不匹配警告。您可以做的最重要的事情之一是重构遗留代码和案例,将警告级别设置为高,并调查并修复所有警告(而不仅仅是通过自由投射!)。

答案 2 :(得分:1)

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

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

对于返回部分,我总是在嵌入式系统中完成此操作。您从函数返回一个值,该值可以表示成功操作。其他值用于显示错误或用于调试。这样一个变量就可以用于所有事情,因为你想避免使用布尔值,你可以使用具有全部潜力的8位变量。

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

如果旧的嵌入式工程师经验丰富和/或聪明,他们可能有充分理由完成所有这些工作。特别是如果他们非常了解硬件。

我希望这对你正在经历的其他代码有所帮助。