The Question: Does doing if(SomeFunction() == TRUE)
instead of doing if(SomeFunction())
protect against some type of coding error? I'm trying to understand if this is protecting from some hidden land-mine, or if it's the result of someone writing code who didn't quite understand how expressions are evaluated. I understand that if done right, both of these things evaluate the same. Just like if(value == 42)
and if(42 == value)
evaluate the same - still, some prefer the 2nd version because it produces a compiler error if someone typo's the == and writes = instead.
问题:是否(SomeFunction()== TRUE)而不是if(SomeFunction())是否可以防止某些类型的编码错误?我试图了解这是否可以防止某些隐藏的地雷,或者是否是某人编写代码并且不太明白如何评估表达式的结果。我明白,如果做得对,这两件事情的评价都是一样的。就像if(value == 42)和if(42 == value)评估相同 - 仍然,有些人更喜欢第二个版本,因为如果某个拼写错误的==而write =而产生编译器错误。
Background: I've inherited some embedded software that was written 4 or 5 years ago by people who don't work here anymore. I'm in the middle of some refactoring to get rid of multi-hundred line functions and global variables and all that jazz, so this thing is readable and we can maintain it going forward. The code is c for a pic microprocessor. This may or may not be relevant. The code has all sorts of weird stuff in it that screams "didn't know what they were doing" but there's a particular pattern (anti-pattern?) in here that I'm trying to understand whether or not there's a good reason for
背景:我继承了一些4年或5年前由不在这里工作的人写的嵌入式软件。我正在进行一些重构以摆脱数百行函数和全局变量以及所有爵士乐,所以这个东西是可读的,我们可以继续保持它。代码是c用于pic微处理器。这可能相关也可能不相关。代码中有各种奇怪的东西,尖叫着“不知道他们在做什么”,但是这里有一个特定的模式(反模式?)我试图理解是否有充分的理由
The Pattern: There are a lot of if statements in here that take the form
模式:这里有很多if语句采用的形式
if(SomeFunction() == TRUE){
. . .
}
Where SomeFunction() is defined as
其中SomeFunction()定义为
BOOLEAN SomeFunction(void){
. . .
if(value == 3)
return(FALSE);
else
return(TRUE);
}
Let's ignore the weird way that SomeFunction returns TRUE or FALSE from the body of an if statement, and the weird way that they made 'return' look like a function invocation.
让我们忽略SomeFunction从if语句的主体返回TRUE或FALSE的奇怪方式,以及他们使'return'看起来像函数调用的奇怪方式。
It seems like this breaks the normal values that c considers 'true' and 'false' Like, they really want to make sure the value returned is equal to whatever is defined as TRUE. It's almost like they're making three states - TRUE, FALSE, and 'something else' And they don't want the 'if' statement to be taken if 'something else' is returned.
这似乎打破了c认为'true'和'false'的正常值。就像,他们真的想确保返回的值等于任何被定义为TRUE的值。这几乎就像他们正在制造三种状态 - 正确,错误和“别的东西”他们不希望在返回“别的东西”的情况下采用'if'语句。
My gut feeling is that this is a weird anti-pattern but I want to give these guys the benefit of the doubt. For example I recognize that if(31 == variable)
looks a little strange but it's written that way so if you typo the == you don't accidently assign 31 to variable. Were the guys that wrote this protecting against a similar problem, or is this just nonsense.
我的直觉是,这是一个奇怪的反模式,但我想给这些家伙带来怀疑的好处。例如,我认识到如果(31 ==变量)看起来有点奇怪,但它是这样编写的,所以如果你输错了==你不会意外地将31分配给变量。如果这些人写这个保护免受类似的问题,或者这只是胡说八道。
Additional Info
- When I wrote this question, I was under the impression that stdbool was not available, but I see now that it's provided by the IDE, just not used in this project. This tilts me more towards "No good reason for doing this."
- It looks like BOOLEAN is defined as
typedef enum _BOOLEAN { FALSE = 0, TRUE } BOOLEAN;
- The development environment in question here is MPLAB 8.6
当我写这个问题时,我的印象是stdbool不可用,但我现在看到它是由IDE提供的,只是在这个项目中没有使用过。这使我更倾向于“没有充分理由这样做”。
看起来BOOLEAN被定义为typedef enum _BOOLEAN {FALSE = 0,TRUE} BOOLEAN;
这里讨论的开发环境是MPLAB 8.6
3 个解决方案
#1
15
Is there a good reason for doing
if(SomeFunction() == true)
instead of doingif(SomeFunction())
有没有理由做if(SomeFunction()== true)而不是if(SomeFunction())
No.
If SomeFunction()
returns a result of type _Bool
, then the equality comparison should be reliable (assuming that the evaluation of the result did not involve undefined behavior). But the use of TRUE
rather than true
, and the type name BOOLEAN
rather than _Bool
or bool
, suggest that the result is not an actual _Bool
(available only in C99 or later), but some ad-hoc Boolean-like type -- perhaps an alias for int
.
如果SomeFunction()返回_Bool类型的结果,那么相等性比较应该是可靠的(假设结果的评估不涉及未定义的行为)。但使用TRUE,而不是真实的,类型名称布尔而不是_Bool或布尔的,表明结果不是实际_Bool(仅适用于C99或更高版本),但一些临时布尔样型 - 也许int的别名。
A value of any scalar type can be used as a condition in an if
statement. If the value is equal to zero, the condition is false; otherwise, the condition is true. If TRUE
is defined as 1
, and SomeFunction()
returns, say, 3
, then the test will fail.
任何标量类型的值都可以用作if语句中的条件。如果该值等于零,则条件为假;否则,条件为真。如果TRUE定义为1,并且SomeFunction()返回(例如3),那么测试将失败。
Writing
if (SomeFunction()) { /* ... */ }
is simpler, clearer, and more likely to behave correctly.
更简单,更清晰,更有可能正常行事。
Note, for example, that the isdigit()
et al functions declared in <ctype.h>
do not just return 0
or 1
; if the argument is a digit, isdigit()
can (and does) return any non-zero value. Code that uses it is expected to handle it correctly -- by not comparing it for equality to 1
, to true
, or to TRUE
.
注意,例如,在
Having said that, there might be a valid reason to compare something for equality to TRUE
-- if it matters whether the result is equal to TRUE
or has some other non-zero value. But in that case, using the names BOOLEAN
and TRUE
is misleading. The whole point of a Boolean type is that values are either true or false; there's no "maybe", and if there happen to be different representations of truth, you shouldn't care which one you have.
话虽如此,可能有正当理由将相等的东西与TRUE进行比较 - 如果结果是等于TRUE还是具有其他非零值则很重要。但在这种情况下,使用名称BOOLEAN和TRUE会产生误导。布尔类型的重点是值为true或false;没有“可能”,如果恰好有不同的真理表示,你就不应该关心你拥有哪一个。
The guideline I try to follow is:
我尝试遵循的指南是:
Never compare a logically Boolean value for equality or inequality to true
or false
(or 0
, 1
, FALSE
, TRUE
). Just test the value directly, with a !
operator if you want to invert the test. (A "logically Boolean" value either is of type _Bool
, or is intended to distinguish between truth and falsehood with no additional information. The latter can be necessary if _Bool
is not available.) Comparison to false
can be safe, but there's no reason to do it; comparing the value directly is still clearer.
切勿将相等或不等式的逻辑布尔值与true或false(或0,1,FALSE,TRUE)进行比较。只需直接测试值即可!如果要反转测试,请运算符。 (“逻辑布尔”值或者是_Bool类型,或者用于区分真假而没有附加信息。如果_Bool不可用,则后者可能是必要的。)与false的比较可以是安全的,但没有理由去做吧;直接比较值仍然更清晰。
And if someone tells you that
如果有人告诉你的话
if (SomeFunction() == true)
is better than
比...更好
if (SomeFunction())
just ask them why
只要问他们为什么
if ((SomeFunction() == true) == true)
isn't even better.
甚至不是更好。
See also section 9 of the comp.lang.c FAQ. Its emphasis on pre-C99 solutions is perhaps a bit dated, but it's still valid.
另请参见comp.lang.c FAQ的第9节。它对C99前解决方案的重视可能有点过时,但它仍然有效。
#2
2
Since in C any non-zero value is considered true and only zero false you should never compare to one specific TRUE
macro definition in any event. It is unnecessarily specific. The form:
因为在C中任何非零值都被认为是真的并且只有零假,所以在任何情况下都不应该与一个特定的TRUE宏定义进行比较。这是不必要的具体。表格:
if( fn() )
is the simplest form, but if you do prefer to compare to a specific value, then only compare to FALSE
thus:
是最简单的形式,但如果您更愿意与特定值进行比较,那么只能与FALSE进行比较:
if( fn() != FALSE ) // Safer than '== TRUE', but entirely unnecessary
which will work for all reasonable definitions of FALSE
and also if fn()
is not BOOLEAN
. But it remains totally unnecessary.
这将适用于FALSE的所有合理定义,如果fn()不是BOOLEAN。但它仍然完全没有必要。
Personally for easier debugging I'd prefer:
个人更容易调试我更喜欢:
BOOLEAN x = fn() ;
if( x )
As well as being able to observe the return value in your debugger before entering or skipping the conditional block, you have the opportunity to name x
something self documenting and specific to the context, which the function name might not reflect. In maintenance you are more likely to maintain a variable name than correct a comment (or many comments). In addition x
is then available to use elsewhere rather then calling fn()
multiple times (which if it has side effects or state may not return the same value).
除了能够在输入或跳过条件块之前观察调试器中的返回值之外,您还可以将x命名为自我记录并且特定于上下文,函数名称可能不会反映出来。在维护中,您更可能维护变量名称而不是更正注释(或许多注释)。此外,x可用于其他地方,而不是多次调用fn()(如果它有副作用或状态可能不会返回相同的值)。
Another problem with user defined boolean types and values is that the definitions may not be consistent throughout - especially if you use third-party code whose authors also thought it a good idea to define their own using the same symbol names as yours. If the names differ (such as BOOL, BOOLEAN or OS_BOOL for example), when your code interfaces to this third-party code, you then have to decide whose boolean type should be used in any particular circumstance, and the names of TRUE and FALSE are likely to * with redefinition warnings or errors.
用户定义的布尔类型和值的另一个问题是定义在整个过程中可能不一致 - 特别是如果您使用第三方代码,其作者也认为使用与您相同的符号名称来定义自己的代码是个好主意。如果名称不同(例如BOOL,BOOLEAN或OS_BOOL),当您的代码与此第三方代码接口时,您必须决定在任何特定环境中应使用哪种布尔类型,以及名称为TRUE和FALSE可能会与重新定义警告或错误发生冲突。
A better approach would be to update the code to use stdbool.h and the real boolean type bool
(an alias for the built in _Bool
in C99) which can have only two values true
and false
. This will still not protect you from the case where fn()
is not a bool
function and returns an integer other then zero or one, but there is then the chance that the compiler will issue a type mismatch warning. One of the best things you can do to refactor legacy code in and case is to set the warning level high and investigate and fix all the warnings (and not just by liberal casting!).
更好的方法是更新代码以使用stdbool.h和真正的布尔类型bool(C99中内置_Bool的别名),它只能有两个值true和false。这仍然不能保护你免受fn()不是bool函数并返回除0或1以外的整数的情况,但是编译器有可能发出类型不匹配警告。你可以做的最重要的事情之一是重构遗留代码和案例是设置警告级别高并调查和修复所有警告(而不仅仅是通过*施法!)。
#3
1
It is probably very late and I am not 100% sure about PICs, but in AVRs some registers are available for application use and they offer very fast read and write speed.
这可能很晚,我对PIC不是100%肯定,但在AVR中,一些寄存器可供应用程序使用,它们提供非常快的读写速度。
The compiler place the Boolean variables there first and then use the SRAM. In order to stop the compiler from doing so, usually a 8-bit variable is used instead of Boolean so that the compiler use the available register for variables used the most in the application. By doing so the application run faster but you should not use Booleans or only when they are used a lot. The only option is to use uint8_t/int8_t or you may use a self made type to act as a Boolean while being a uint8_t/int_t.
编译器首先放置布尔变量,然后使用SRAM。为了阻止编译器这样做,通常使用8位变量而不是布尔值,以便编译器将可用寄存器用于应用程序中使用最多的变量。通过这样做,应用程序运行得更快,但您不应该使用布尔值,或仅在它们被大量使用时使用。唯一的选择是使用uint8_t / int8_t,或者您可以使用自制类型作为布尔值,同时作为uint8_t / int_t。
For the return section, I have always done this in embedded systems. You return one value from the function and that value can represent successful operation. Other values are used to show errors or for debugging. This way one variable is used for everything and since you would want to avoid using Boolean, you can use the 8-bit variable with full potential.
对于返回部分,我一直在嵌入式系统中完成此操作。您从函数返回一个值,该值可以表示成功的操作。其他值用于显示错误或用于调试。这样一个变量就可以用于所有事情,因为你想避免使用布尔值,你可以使用具有全部潜力的8位变量。
Overall I would like to emphasize something here, the embedded folks care about speed and memory a lot more than software engineers. I have worked with both folks, embedded system engineers generate a garbage looking code but it runs fast and consume very little memory. One of the reason that the code looks bad/ugly is because the compilers are not as smart as the typical compilers used by software engineers and sometimes special tweaks (which may look stupid) results in a more efficient assembly code. On the other hand, software engineers spend more time on readability and may not care about the efficiency of the code as much because the compiler will do most of the job.
总的来说,我想强调一下,嵌入式人员比软件工程师更关心速度和内存。我和两个人一起工作过,嵌入式系统工程师生成一个看起来很垃圾的代码,但它运行速度很快,占用的内存非常少。代码看起来很糟糕/丑陋的原因之一是因为编译器不像软件工程师使用的典型编译器那样聪明,有时特殊的调整(可能看起来很愚蠢)会导致更高效的汇编代码。另一方面,软件工程师花费更多时间在可读性上,并且可能不关心代码的效率,因为编译器将完成大部分工作。
If the old embedded engineers were experienced and/or smart, they have probably done all of these for a good reason. Specially if they know the hardware very well.
如果旧的嵌入式工程师经验丰富和/或聪明,他们可能有充分理由完成所有这些工作。特别是如果他们非常了解硬件。
I hope this help for the rest of the code you are going through.
我希望这对你正在经历的其他代码有所帮助。
#1
15
Is there a good reason for doing
if(SomeFunction() == true)
instead of doingif(SomeFunction())
有没有理由做if(SomeFunction()== true)而不是if(SomeFunction())
No.
If SomeFunction()
returns a result of type _Bool
, then the equality comparison should be reliable (assuming that the evaluation of the result did not involve undefined behavior). But the use of TRUE
rather than true
, and the type name BOOLEAN
rather than _Bool
or bool
, suggest that the result is not an actual _Bool
(available only in C99 or later), but some ad-hoc Boolean-like type -- perhaps an alias for int
.
如果SomeFunction()返回_Bool类型的结果,那么相等性比较应该是可靠的(假设结果的评估不涉及未定义的行为)。但使用TRUE,而不是真实的,类型名称布尔而不是_Bool或布尔的,表明结果不是实际_Bool(仅适用于C99或更高版本),但一些临时布尔样型 - 也许int的别名。
A value of any scalar type can be used as a condition in an if
statement. If the value is equal to zero, the condition is false; otherwise, the condition is true. If TRUE
is defined as 1
, and SomeFunction()
returns, say, 3
, then the test will fail.
任何标量类型的值都可以用作if语句中的条件。如果该值等于零,则条件为假;否则,条件为真。如果TRUE定义为1,并且SomeFunction()返回(例如3),那么测试将失败。
Writing
if (SomeFunction()) { /* ... */ }
is simpler, clearer, and more likely to behave correctly.
更简单,更清晰,更有可能正常行事。
Note, for example, that the isdigit()
et al functions declared in <ctype.h>
do not just return 0
or 1
; if the argument is a digit, isdigit()
can (and does) return any non-zero value. Code that uses it is expected to handle it correctly -- by not comparing it for equality to 1
, to true
, or to TRUE
.
注意,例如,在
Having said that, there might be a valid reason to compare something for equality to TRUE
-- if it matters whether the result is equal to TRUE
or has some other non-zero value. But in that case, using the names BOOLEAN
and TRUE
is misleading. The whole point of a Boolean type is that values are either true or false; there's no "maybe", and if there happen to be different representations of truth, you shouldn't care which one you have.
话虽如此,可能有正当理由将相等的东西与TRUE进行比较 - 如果结果是等于TRUE还是具有其他非零值则很重要。但在这种情况下,使用名称BOOLEAN和TRUE会产生误导。布尔类型的重点是值为true或false;没有“可能”,如果恰好有不同的真理表示,你就不应该关心你拥有哪一个。
The guideline I try to follow is:
我尝试遵循的指南是:
Never compare a logically Boolean value for equality or inequality to true
or false
(or 0
, 1
, FALSE
, TRUE
). Just test the value directly, with a !
operator if you want to invert the test. (A "logically Boolean" value either is of type _Bool
, or is intended to distinguish between truth and falsehood with no additional information. The latter can be necessary if _Bool
is not available.) Comparison to false
can be safe, but there's no reason to do it; comparing the value directly is still clearer.
切勿将相等或不等式的逻辑布尔值与true或false(或0,1,FALSE,TRUE)进行比较。只需直接测试值即可!如果要反转测试,请运算符。 (“逻辑布尔”值或者是_Bool类型,或者用于区分真假而没有附加信息。如果_Bool不可用,则后者可能是必要的。)与false的比较可以是安全的,但没有理由去做吧;直接比较值仍然更清晰。
And if someone tells you that
如果有人告诉你的话
if (SomeFunction() == true)
is better than
比...更好
if (SomeFunction())
just ask them why
只要问他们为什么
if ((SomeFunction() == true) == true)
isn't even better.
甚至不是更好。
See also section 9 of the comp.lang.c FAQ. Its emphasis on pre-C99 solutions is perhaps a bit dated, but it's still valid.
另请参见comp.lang.c FAQ的第9节。它对C99前解决方案的重视可能有点过时,但它仍然有效。
#2
2
Since in C any non-zero value is considered true and only zero false you should never compare to one specific TRUE
macro definition in any event. It is unnecessarily specific. The form:
因为在C中任何非零值都被认为是真的并且只有零假,所以在任何情况下都不应该与一个特定的TRUE宏定义进行比较。这是不必要的具体。表格:
if( fn() )
is the simplest form, but if you do prefer to compare to a specific value, then only compare to FALSE
thus:
是最简单的形式,但如果您更愿意与特定值进行比较,那么只能与FALSE进行比较:
if( fn() != FALSE ) // Safer than '== TRUE', but entirely unnecessary
which will work for all reasonable definitions of FALSE
and also if fn()
is not BOOLEAN
. But it remains totally unnecessary.
这将适用于FALSE的所有合理定义,如果fn()不是BOOLEAN。但它仍然完全没有必要。
Personally for easier debugging I'd prefer:
个人更容易调试我更喜欢:
BOOLEAN x = fn() ;
if( x )
As well as being able to observe the return value in your debugger before entering or skipping the conditional block, you have the opportunity to name x
something self documenting and specific to the context, which the function name might not reflect. In maintenance you are more likely to maintain a variable name than correct a comment (or many comments). In addition x
is then available to use elsewhere rather then calling fn()
multiple times (which if it has side effects or state may not return the same value).
除了能够在输入或跳过条件块之前观察调试器中的返回值之外,您还可以将x命名为自我记录并且特定于上下文,函数名称可能不会反映出来。在维护中,您更可能维护变量名称而不是更正注释(或许多注释)。此外,x可用于其他地方,而不是多次调用fn()(如果它有副作用或状态可能不会返回相同的值)。
Another problem with user defined boolean types and values is that the definitions may not be consistent throughout - especially if you use third-party code whose authors also thought it a good idea to define their own using the same symbol names as yours. If the names differ (such as BOOL, BOOLEAN or OS_BOOL for example), when your code interfaces to this third-party code, you then have to decide whose boolean type should be used in any particular circumstance, and the names of TRUE and FALSE are likely to * with redefinition warnings or errors.
用户定义的布尔类型和值的另一个问题是定义在整个过程中可能不一致 - 特别是如果您使用第三方代码,其作者也认为使用与您相同的符号名称来定义自己的代码是个好主意。如果名称不同(例如BOOL,BOOLEAN或OS_BOOL),当您的代码与此第三方代码接口时,您必须决定在任何特定环境中应使用哪种布尔类型,以及名称为TRUE和FALSE可能会与重新定义警告或错误发生冲突。
A better approach would be to update the code to use stdbool.h and the real boolean type bool
(an alias for the built in _Bool
in C99) which can have only two values true
and false
. This will still not protect you from the case where fn()
is not a bool
function and returns an integer other then zero or one, but there is then the chance that the compiler will issue a type mismatch warning. One of the best things you can do to refactor legacy code in and case is to set the warning level high and investigate and fix all the warnings (and not just by liberal casting!).
更好的方法是更新代码以使用stdbool.h和真正的布尔类型bool(C99中内置_Bool的别名),它只能有两个值true和false。这仍然不能保护你免受fn()不是bool函数并返回除0或1以外的整数的情况,但是编译器有可能发出类型不匹配警告。你可以做的最重要的事情之一是重构遗留代码和案例是设置警告级别高并调查和修复所有警告(而不仅仅是通过*施法!)。
#3
1
It is probably very late and I am not 100% sure about PICs, but in AVRs some registers are available for application use and they offer very fast read and write speed.
这可能很晚,我对PIC不是100%肯定,但在AVR中,一些寄存器可供应用程序使用,它们提供非常快的读写速度。
The compiler place the Boolean variables there first and then use the SRAM. In order to stop the compiler from doing so, usually a 8-bit variable is used instead of Boolean so that the compiler use the available register for variables used the most in the application. By doing so the application run faster but you should not use Booleans or only when they are used a lot. The only option is to use uint8_t/int8_t or you may use a self made type to act as a Boolean while being a uint8_t/int_t.
编译器首先放置布尔变量,然后使用SRAM。为了阻止编译器这样做,通常使用8位变量而不是布尔值,以便编译器将可用寄存器用于应用程序中使用最多的变量。通过这样做,应用程序运行得更快,但您不应该使用布尔值,或仅在它们被大量使用时使用。唯一的选择是使用uint8_t / int8_t,或者您可以使用自制类型作为布尔值,同时作为uint8_t / int_t。
For the return section, I have always done this in embedded systems. You return one value from the function and that value can represent successful operation. Other values are used to show errors or for debugging. This way one variable is used for everything and since you would want to avoid using Boolean, you can use the 8-bit variable with full potential.
对于返回部分,我一直在嵌入式系统中完成此操作。您从函数返回一个值,该值可以表示成功的操作。其他值用于显示错误或用于调试。这样一个变量就可以用于所有事情,因为你想避免使用布尔值,你可以使用具有全部潜力的8位变量。
Overall I would like to emphasize something here, the embedded folks care about speed and memory a lot more than software engineers. I have worked with both folks, embedded system engineers generate a garbage looking code but it runs fast and consume very little memory. One of the reason that the code looks bad/ugly is because the compilers are not as smart as the typical compilers used by software engineers and sometimes special tweaks (which may look stupid) results in a more efficient assembly code. On the other hand, software engineers spend more time on readability and may not care about the efficiency of the code as much because the compiler will do most of the job.
总的来说,我想强调一下,嵌入式人员比软件工程师更关心速度和内存。我和两个人一起工作过,嵌入式系统工程师生成一个看起来很垃圾的代码,但它运行速度很快,占用的内存非常少。代码看起来很糟糕/丑陋的原因之一是因为编译器不像软件工程师使用的典型编译器那样聪明,有时特殊的调整(可能看起来很愚蠢)会导致更高效的汇编代码。另一方面,软件工程师花费更多时间在可读性上,并且可能不关心代码的效率,因为编译器将完成大部分工作。
If the old embedded engineers were experienced and/or smart, they have probably done all of these for a good reason. Specially if they know the hardware very well.
如果旧的嵌入式工程师经验丰富和/或聪明,他们可能有充分理由完成所有这些工作。特别是如果他们非常了解硬件。
I hope this help for the rest of the code you are going through.
我希望这对你正在经历的其他代码有所帮助。