In this article, readability will be the main theme, as we look into how to make conditional expressions readable.
The problem with conditional expressions is that they too often become large, unstructured, and thus impossible to read. Several studies have shown that complex conditional expressions often cause problems in programs. By making conditional expressions easy to read, it will be easier for both the programmers and people who inspect the code to ensure that the code is correct.
So, what is a conditional expression? Well, it is the expression used by if, while and a number of other C constructions to decide what they should do.
In the following example, the expression a && b is a conditional expression. It simply checks that both the a and b conditions are fulfilled:
if (a && b)
printf("Both a and b were true\n");
Let's look at a real-world example of something that isn't that easy to read. Please note that this piece of code is still a light-weight compared to others I have seen over the years. Later in the article we will rewrite this statement into a more readable form.
if (info.isNameCandidate && isType (token, TOKEN_PAREN_NAME) &&
! st->gotParenName &&
(! info.isParamList || ! st->haveQualifyingName ||
c == '(' ||
(c == '=' && st->implementation != IMP_VIRTUAL) ||
(st->declaration == DECL_NONE && isOneOf (c, ",;"))))
The first method is to use indentation and parentheses to make complex conditional expressions readable.
One problem is that the individual operators (such as &&) often are placed at the end of lines, or in the middle of lines. This way there is no visible clue to the structure of the expression.
Here is an example of a poor structure:
if ((FirstCondition && SecondCondition) || ThirdCondition ||
We can rewrite this code so that the operators indicate the structure of the conditional expression. Unlike above, it is apparent that the top-level of the expression has three terms, and that the first term itself has two sub-terms:
if ( ( FirstCondition
Let's revisit the complicated conditional expression presented earlier. If we rewrite it using the technique presented here, it would look like this:
if ( info.isNameCandidate
&& isType (token, TOKEN_PAREN_NAME)
&& ! st->gotParenName
&& ( ! info.isParamList
|| ! st->haveQualifyingName
|| c == '('
|| ( c == '='
&& st->implementation != IMP_VIRTUAL)
|| ( st->declaration == DECL_NONE
&& isOneOf (c, ",;"))))
Even if you browse quickly through the code, you can immediately see the structure of this conditional expression. At the top level there is a conjunction (which is a fancy word for things that are and-ed together). The last term is a disjunction (another fancy word for terms that are or -ed together). The or expression itself is built using simpler and expressions.
Another source of trouble is when different logical operators are used in the same expression without proper use of parentheses. Let's look at the following example. At first glance, it looks like an and expression. However, as we later will see, it's not.
if (FirstCondition && SecondCondition || ThirdCondition && FourthCondition)
If we should rewrite it with proper indentation using the operators it would look like as follows:
if ( FirstCondition
The fact that &&s and ||s are placed directly under each other should act as a warning signal on second thought, it should act as a FIRE ALARM signal. Mixing || and && without proper parentheses is a road to destruction, no matter what your local expert tells you!
Add the parentheses (remember that && binds stronger than ||) and re-indent. This will be the result:
if ( ( FirstCondition
|| ( ThirdCondition
At last the meaning of the expression is crystal clear; it is an or expression with two terms, constructed using two and terms each.
Please note that this indentation technique is not limited to ||s and &&s. Expressions containing == and other comparison operators can easily be indented in the same way, as can normal arithmetical operators. For example:
if ( player_passed_the_test
&& ( high_score
>= ( index
* ( the_alpha_value
In some situations it might not be easy to express a condition using one complex expression. In these cases it is useful to introduce a control variable. When using a control variable the code is divided into two parts—the first part decides if an action should be performed by assigning a value to the control variable and the second part performs the action.
For example (in C, make sure stdbool.h is included):
/* Start by assuming that we should perform The Action. */
bool doIt = true;
* When First Condition is true, we should not perform The Action
* (unless decided otherwise below).
doIt = false;
* If the Second Condition is true, we decide that The Action
* should be performed.
doIt = true;
* Of course, The Action should not be performed if the Third
* Action is met.
doIt = false;
/* Perform the action. */
In this article I have introduced two techniques for making conditional expressions easier to read. The techniques can be applied when performing refactoring of existing code, or when new code is written. In my opinion, the best way to fight problems in production code is to make sure that it is apparent to anyone that reads the code what the code does and how it does it.
This article is written by Anders Lindgren, Development Engineer at IAR Systems.