
Logical expression in programming is a programming language design, the result of which is “true” or “false”. Many programming books designed to learn a language from scratch provide possible operations on the logical expressions that every novice developer has encountered. In this article I will not tell you that the AND operator is more priority than the OR operator. I will talk about common errors in simple conditional expressions consisting of only three operators, and show you how to check your code with the help of constructing truth tables. The described errors make the developers of such famous projects as FreeBSD, Microsoft ChakraCore, Mozilla Thunderbird, LibreOffice, and many others.
Introduction
I am developing a static code analyzer for C / C ++ / C # languages -
PVS-Studio . In my work I have to deal a lot with open and closed code of different projects. Often the result of this work are articles on
checking open source projects containing a description of errors found and shortcomings. After viewing a large amount of code, you begin to notice the various error patterns that programmers allow. So, my colleague Andrei Karpov wrote an article about the
effect of the last line after finding a large number of errors made in the last fragments of the same type of code.
At the beginning of this year, I checked with the help of the analyzer many projects of large IT companies, which, following the current trend, spread the open source of their projects under free licenses. I began to notice that in almost every project there is an error in the conditional expression due to the incorrect recording of conditional statements. The expression itself is quite simple and consists of only three operators:
- ! = || ! =
- == || ! =
- == && ==
- == &&! =
In total, there are 6 such conditional expressions, but 4 of them are erroneous: two are always true or false; in two, the result of the entire expression does not depend on the result of the subexpression contained in it.
To prove the wrong result of the expression, I will build a truth table in each example; I will also give for each example one piece of code from an open project. This article will also mention the ternary operator '?:', Which has almost the lowest priority of all operators, but so many developers are not aware of this.
')
Since most often I have encountered incorrect conditional expressions when checking the result of different functions, the return code of which is compared with error codes, then in the synthetic examples given below I will use a variable called
err , and
code1 and
code2 will be constants. In this case, the constants
code1 and
code2 are not equal. The value
“other codes” will mean any other constants not equal to
code1 and
code2 .
Errors using the '||' operator
Expression! = || ! =
A synthetic example in which the result of the conditional expression will always be true:
if ( err != code1 || err != code2) { .... }
The following is the truth table for this code example:
Now let's look at a real example of the error found in the LibreOffice project.
V547 Expression is always true. Probably the '&&' operator should be used here. sbxmod.cxx 1777
enum SbxDataType { SbxEMPTY = 0, SbxNULL = 1, .... }; void SbModule::GetCodeCompleteDataFromParse( CodeCompleteDataCache& aCache) { .... if( (pSymDef->GetType() != SbxEMPTY) ||
Expression == || ! =
A synthetic example in which the result of the entire conditional expression does not depend on the result of the subexpression
(err == code1) :
if ( err == code1 || err != code2) { .... }
The following is the truth table for this code example:
Now let's look at a real example of an error found in the FreeBSD project.
V590 Consider inspecting the 'error == 0 || error! = - 1 'expression. The expression is misprint. nd6.c 2119
int nd6_output_ifp(....) { .... error = send_sendso_input_hook(m, ifp, SND_OUT, ip6len); if (error == 0 || error != -1)
It is not much different from a synthetic example, is it?
Errors using the '&&' operator
Expression == && ==
A synthetic example in which the result of a conditional expression will always be false:
if ( err == code1 && err == code2) { .... }
The following is the truth table for this code example:
Now let's take a look at a real example of an error found in the SeriousEngine project.
V547 Expression is always false. Probably the '||' operator should be used here. entity.cpp 3537
enum RenderType { .... RT_BRUSH = 4, RT_FIELDBRUSH = 8, .... }; void CEntity::DumpSync_t(CTStream &strm, INDEX iExtensiveSyncCheck) { .... if( en_pciCollisionInfo == NULL) { strm.FPrintF_t("Collision info NULL\n"); } else if (en_RenderType==RT_BRUSH &&
Expression == &&! =
A synthetic example in which the result of the entire conditional expression does not depend on the result of the subroutine “err! = Code2”:
if ( err == code1 && err != code2) { .... }
The following is the truth table for this code example:
Now let's look at a real example of an error found in the ChakraCore project - a JavaScript engine for Microsoft Edge.
V590 Consider inspecting the 'sub [i]! =' - '&& sub [i] ==' / '' expression. The expression is misprint. rl.cpp 1388
const char * stristr ( const char * str, const char * sub ) { .... for (i = 0; i < len; i++) { if (tolower(str[i]) != tolower(sub[i])) { if ((str[i] != '/' && str[i] != '-') || (sub[i] != '-' && sub[i] == '/')) { / <=
Errors using the '?:' Operator
V502 Perhaps the '?:' Operator was different. The '?:' Operator has a lower than the '|' operator. ata-serverworks.c 166
static int ata_serverworks_chipinit(device_t dev) { .... pci_write_config(dev, 0x5a, (pci_read_config(dev, 0x5a, 1) & ~0x40) | (ctlr->chip->cfg1 == SWKS_100) ? 0x03 : 0x02, 1); } .... }
In conclusion, I want to say about the ternary operator '?:'. Its priority is almost the lowest among all operators. Only assignment,
throw and comma operator are lower. The error in the example was found in the FreeBSD kernel. Here, the ternary operator was used to select the desired flag and to write a short beautiful code. But the priority of the bitwise OR operator is higher, so the conditional expression is not calculated in the order in which the programmer planned. I also decided to describe this error in this article, since it is very common among the projects I have verified.
Conclusion
The described patterns of conditional expressions can be very dangerous if you don’t take extra care when writing code. Despite the small number of statements, the entire conditional expression may be misunderstood. The code in which such an error was made may look quite logical and will be skipped after the code review. You can insure yourself against such errors by checking your code with the help of building truth tables, if you have doubts about the correctness of the condition, as well as with the help of regular checks with static analyzers.
If you want to share this article with an English-speaking audience, then please use the link to the translation: Svyatoslav Razmyslov.
Logical Expressions in C / C ++. Mistakes Made by Professionals .