To Brace Or Not To Brace

Summary

In this article I discuss my opinions on when braces should be used to delineate blocks of code in C and C++. In addition I discuss my views on where in the code the braces should be placed. I use examples from thirteen years of experience in C and C++ programming to back up my opinions.

Discussion

One commonly debated topic in C and C++ programming is whether or not braces should be used with if, while, and for statements in C and C++. The debate stems from the fact that if the if, while, or for statement requires exactly one statement after the test, braces are not required. For example, the following is allowed in the C and C++ language specifications:

if ({test})
    {statement}

while ({test})
    {statement}

for ({start}; {test}; {next})
    {statement}

According to the C and C++ language specifications braces are only considered mandatory if more than one statement is to be executed when the {test} evaluates to true. In fact, the C and C++ language specifications allow {statement} to be on the same line as the {test}.

However, it is my professional opinion that {statement} should never be placed on the same line as the {test}. In addition, braces should be considered mandatory.

First I will discuss the basis for my opinion that {statement} should never be placed on the same line as the {test} in if, for, and while statements.

Consider the following code snippet:

if (foo()) bar();
    baz();

When tracing through this code snippet in a debugger the debugger will stop on the

if (foo()) bar();

line. When the user uses the “step over” command, the debugger stops on the

baz();

line. The debugger gives no indication of whether or not the function bar was ever called.

If this code snippet were written as follows,

if (foo())
    bar();
baz();

the following will happen as the user steps through the code. First, the debugger will stop on the

if (foo())

line. When the user uses the “step over” command, the debugger will stop on the bar line if foo returned true. Otherwise the debugger will stop on the baz line next. By simply changing the formatting so that the call to bar is on its own line the code becomes much easier to debug and the user no longer has any doubt about whether or not the function bar was called. For this reason, the {statement} should never be placed on the same line as the {test} of a if, for, or while statement.

Some will argue that the user can use the step in command to determine if bar is called in the original version of the if statement. However, it is not practical to do so. This is because the first time the step in command is used on the

if (foo()) bar();

line, the debugger will step into foo. The user will then have to use the step out command to return to the function containing the if statement and use the step in command again to determine whether or not bar is called.

Matters are worse if the {test} of the if statement is more complicated such as the following:

if ((foo1() || foo2() || foo3()) && foo4()) bar();

In this case the user will need to use the step in, step out, step in sequence as many as four times just to find out if bar is called. Expecting someone to go to this much trouble to determine if a single function is called is simply unreasonable.

Next I will discuss the basis for my opinion that braces should be considered mandatory.

First, changes over time are easier to track if braces are considered to be mandatory. Consider the following function in which the if statement is written without braces:

/* revision 1 */
void fun()
{
    if (foo())
        bar();
    baz();
}

Every application changes over time. Lets say that the function changes so that the function bar1 needs to be called in addition to the function bar if foo returns true. The function fun becomes as follows:

/* revision 2 */
void fun()
{
    if (foo())
    {
        bar();
        bar1();
    }
    baz();
}

If you use a tool such as diff to determine what the changes between revision 1 and 2 of this function, it will indicate that three lines of code changed. The first change is the addition of the open brace. The second change is the addition of the call to bar1 after the call to bar. The third change is the addition of the closing brace. However, there was only one line of code that changed the actual functionality of the function fun.

If revision 1 of fun were written as follows:

/* revision 1 */
void fun()
{
    if (foo())
    {
        bar();
    }
    baz();
}

then diff would indicate that only one line had changed.

Next considering braces to be mandatory protects you from possible mistakes by developers making changes to your code when they are in a hurry and under a lot of pressure. Consider the original version of the function fun listed above. Lets assume that a developer wished to modify the function so that it would write a message to a log file when fun is about to call bar, but they are in a hurry or perhaps had just finished a task in Python which uses indentation and not braces to delineate code blocks and forget to add the braces. Then the function becomes as follows:

/* revision 2 */
void fun()
{
    if (foo())
        log("fun calling bar because foo returned TRUE.");
        bar();
    baz();
}

This code will compile without warnings. However, it will change the behavior of fun in an obviously unwanted way in that fun is now calling bar even if foo does not return TRUE. Fortunately it is easy to tell that this change in behavior was not intended in this case.

The problem becomes more complicated in situations in which instead of adding code to log the function call, the task is to have a function be called before bar if foo returns TRUE. Again lets assume that the developer is in a hurry or still has Python on his mind so he forgets to add the braces. Then the function fun becomes as follows:

/* revision 2 */
void fun()
{
    if (foo())
        fun1();
        bar();
    baz();
}

This code will also compile without any warnings. However, determining if this change is in error is not as easy as in the first case in which the change was the addition of a line of code intended for logging. By just looking at the code can you tell with 100% certainty that the developer who made this change did not intend to change the function fun so that bar is called all the time without asking the developer who made the change? If you are using a source control tool such as subversion to track changes to your software over time and the developer provides detailed change descriptions it is possible that you could. However, under most circumstances, you could not be 100% certain that the change in behavior was not intentional without talking to the developer who made the change. Then what will you do if the developer had died or is unavailable for some other reason?

If braces are considered mandatory, this problem will never come up in your project.

The final reason that braces should be considered mandatory is that it eases code navigation in modern text editors. Most modern text editors have a brace matching capability that allows you to jump to the matching brace. In if, for, and while statements this lets you jump to the end of the statement with a single command. For simple if, for, and while statements this makes no difference. However, there are cases in which the braces for one statement are optional rule is misused and code is written like this.

if ({test})
    if ({test1})
        if ({test2})
            for ({start}; {test3}; {next})
            {
                /*
                several thousand lines of code
                */
            }

In the case that you are reading through this code and you know that {test} does not return TRUE, you do not care about what happens if {test} returns TRUE. You want to move past the for loop to find out what happens if {test} returns FALSE. If braces were present for the “if ( {test} )” statement, you could simply press down arrow once and then use the move to matching brace command to move on to that section of code. However, there are no braces so you have to arrow down four times before using the move to matching brace command. If this same code were written as follows, the extra three keystrokes would not be necessary.

if ({test})
{
    if ({test1})
    {
        if ({test2})
        {
            for ({start}; {test3}; {next})
            {
                /*
                several thousand lines of code
                */
            }
        }
    }
}

There is also a debate about where the braces should be placed in code. In all my examples the opening brace is located on its own line. However, many programmers prefer to place the opening brace at the end of the if, for, or while line as follows:

if ({test}) {
    {statement}
}

This is perfectly legal according to the C and C++ language specifications. However it is my opinion that this should never be done, that the opening brace should always be placed on its own line. Consider the following:

if ({AVeryLongAndComplicatedTestThatGoesOffTheRightEdgeOfTheScreen}) {
    {statement}
    {statement1}
    /*
    several thousand more lines of code
    */
}

In this case, assuming that the test actually does go off the right edge of the screen, can you tell with absolute certainty that {statement1} and the several thousand additional lines of code will only get called if the test returns TRUE without going to the trouble of using the end key to determine whether or not the if line ends in a brace? Simply depending on indentation is not an accurate indicator. This is because the C and C++ language specification allows for different levels of indentation to be used in the same block of code. For example the following is legal in C and C++.

if ({test})
{
    {statement}
        {statement1}
{statement3}
    {statement4}
}

The fact is that in code where braces are placed at the end of the if, for, or while line, someone reading the code must go through the trouble to using the end key every time a if, for, or while line is encountered that goes off the right edge of the screen in order to determine whether or not multiple lines of code or a single line of code gets called when the test returns TRUE. This simply makes the job of reviewing the code much more difficult.

Too summarize, braces should be considered mandatory in if, for, and while statements in order to make tracking changes over time easier, to protect you from the harried programmer phenomena, and to make navigating through your code easier. In addition, the {statement} should never be placed on the same line as the {test} in if, for, or while statements in order to make it easier to debug your code. Finally, braces should never be placed at the end of the if, for, or while line in order to make it easier to determine whether one statement or many statements get called if the {test} returns TRUE when the test is long enough that it actually goes off the right edge of the screen.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.