Embercode
✕
C++ ConditionalsLesson 12 of 14

40 min read

Dangling else and braces

Why this matters

An if statement runs its body when a condition is true. An else gives a second body that runs when the condition is false. Trouble starts when one if sits inside another. Then the compiler has to decide which if an else belongs to. C++ uses one fixed rule for this, and the rule ignores indentation, the spaces at the start of each line that show how a person reads the code.

This bug is called the dangling else. The else hangs off the code with no clear owner. The program compiles, runs and prints output, so nothing tells you that a branch is wrong. It fails only for some inputs, and those inputs are often the ones you did not test.

The fix is short. Put braces around each body, so the owner of every else is written in the code and not left to a rule. This lesson covers the pairing rule, three worked examples, the braces that prevent the bug, and the compiler messages you will see when the structure is broken. It does not repeat the else if chains from the nesting lesson earlier in this module, and it does not cover the order of && checks, which the next lesson covers.

The idea: an else goes with the nearest if

Start with the simplest case, one if and one else:

C++ · editable

The output is:

positive

The body of an if is the statement right after the condition. Without braces, that body is exactly one statement. Here the body of the if is the cout line that ends with a semicolon, and the body of the else is the cout line under it. In this case there is only one if, so the else has only one place to go.

Now add a second if inside the first one:

if (age >= 18)
    if (has_ticket)
        cout << "Entry allowed" << endl;
else
    cout << "Entry refused" << endl;

Read this the way a person reads the indentation. The else lines up with the outer if (age >= 18), so it looks like it belongs to the outer if. The compiler does not read it that way. It uses the rule from the next paragraph, and the rule gives a different answer.

When the compiler reads an else, it looks back for the nearest if that does not have an else yet, and pairs the two. That is the whole rule. In the code above, the else comes after the inner if (has_ticket). The inner if has no else yet, so the else belongs to has_ticket. The outer if (age >= 18) gets no else at all.

Two things follow from this rule. First, the indentation has no effect on the pairing. Second, the else attaches to the inner if even when it is written at the outer level. The name "dangling" comes from this: the else is written where it looks like it should go, but the compiler attaches it to the closest if that can take it.

Worked example 1: an else that goes to the wrong if

Here is a rule for a door. Adults, meaning people who are 18 or older, can enter when they have a ticket. Everyone else is refused. The programmer wrote the code below. The else is meant to refuse anyone who fails the age check, but it is written after the inner if.

C++ · editable

The output is:

age 15, ticket yes: age 20, ticket no: Entry refused
age 20, ticket yes: Entry allowed

Look at the first line. The age 15 call printed its label and then nothing else. The else belongs to has_ticket, so when the outer age >= 18 is false, the whole inner if is skipped, and its else is skipped with it. No text is printed, and because the function does not end the line itself, the next call continues on the same line. The second call, age 20 with no ticket, is handled by the else on the inner if, and it prints "Entry refused". That line is correct by accident. The intended refusal for age 15 never happens.

The compiler gives a warning for this code when you build with -Wall:

main.cpp: In function ‘void check(int, bool)’:
main.cpp:8:8: warning: suggest explicit braces to avoid ambiguous ‘else’ [-Wdangling-else]
    8 |     if (age >= 18)
      |        ^

The warning points at the outer if, which is the one that the reader expects to own the else. Treat this warning as an error in your own code.

Here is the fixed version. The braces around the inner if close it before the else arrives, so the else can only pair with the outer if:

C++ · editable

The output is:

age 15, ticket yes: Entry refused
age 20, ticket no: Entry refused
age 20, ticket yes: Entry allowed

Now every case prints a result, and the age 15 case is refused as the rule says.

Worked example 2: three ifs and two elses

The next example is harder. It has three if statements in a row, each inside the one before, and two else lines. The rule for the compiler is the same. Work out which if each else belongs to by starting at the else and walking up. Skip any if that already has an else. The first if you reach is the owner.

C++ · editable

Walk through the code with the rule. The first else, the one that prints "B", comes after if (c == 1). That if has no else yet, so "B" is its else. The second else, the one that prints "C", comes after the if (b == 1) line. The if (c == 1) already has an else, so the walk skips it, and the next if is if (b == 1). That if gets "C". The outer if (a == 1) gets no else.

The output is:

a=0 b=0 c=0 ->
a=1 b=0 c=0 -> C
a=1 b=1 c=1 -> A
a=1 b=1 c=0 -> B

The table below turns the output into a rule you can check by hand:

abcprints
000nothing
100C
111A
110B

The first row prints nothing, because a == 1 is false and the outer if has no else. The second row shows the case that surprises most readers. b == 1 is false, so the if (b == 1) body is skipped. Its else runs and prints "C". The text "B" is never reached, even though it sits lower in the code.

The compiler warns about this code too, with the same message as before. When the structure is this deep, braces make it possible to read the code without walking up the lines.

Worked example 3: the same bug inside a loop

The rule does not change inside a loop. The body of a for loop is one statement, and an if statement is one statement, so the same pairing applies. This example reads a list of numbers. The intended rules are: report large positive numbers, report numbers that are not positive, and skip small positive numbers.

C++ · editable

The output is:

500: large positive
5: not positive

The number 500 is handled correctly. The number 5 is wrong: it is positive, so it should be skipped, but the output says "not positive". The reason is the same as before. The else belongs to if (x > 100), the nearest if with no else. For 5, the outer test is true and the inner test is false, so the inner else runs. For -3, the outer test is false, so the body of the outer if is skipped. The else on the inner if never runs, so -3 is never reported at all.

Here is the fixed version. Each body has braces, and the inner if is closed before the outer else begins:

C++ · editable

The output is:

500: large positive
-3: not positive

Now -3 is reported and 5 is skipped, which matches the stated rule.

Worked example 4: correct code that is hard to read

Not every dangling else is a bug. Sometimes the pairing is exactly what the author meant, and the code works. The problem is that a reader has to work out the pairing before they can trust the code, and a later edit can break it without anyone noticing. This example computes a late fee for a library book. One unit is charged per day late, up to 10 units, and members pay half.

C++ · editable

The output is:

0 days, guest: 0
5 days, guest: 5
12 days, guest: 10
12 days, member: 5
5 days, member: 2

Before trusting the output, write down the pairs. The else comes after if (days_late > 10), and that if has no else yet, so the two belong together. The outer if (days_late > 0) has no else. That is the pairing the author wanted: a fee of 5 for 5 days, and a cap of 10 for 12 days. The output matches the rule, so the code is correct.

The compiler still warns about this code, because it cannot know what the author meant. It warns about the outer if, because the else could belong to either one. Adding braces removes the warning and makes the pairing visible to the next reader. This is a method you can use on any snippet you do not know well:

  1. Find every else in the snippet.
  2. For each else, walk up to the nearest if with no else yet, and mark the pair.
  3. Check that each pair matches the rule the code is meant to follow.
  4. If the snippet will be edited later, or you are not sure, add braces.

How braces prevent the bug

A pair of braces, { and }, makes a block. A block counts as one statement and has a clear end. When the compiler reaches the closing } of an inner if, that if is finished. An else that follows the } cannot pair with it, because the if is already closed, so it pairs with the next open if above.

The rule for writing your own code is short. Put braces around the body of every if and every else, even when the body is one line. Then the nearest-if rule never has a choice to make, because every inner if that sits in front of an else is closed by its own braces.

A common question is whether braces are needed when there is no inner if. A plain if and else with one statement each works without braces, as the first example showed. Braces are still the safer habit. The cost is a few lines. The benefit is that adding a second statement later does not change the meaning of the code, which is the next mistake in this lesson.

Braces must wrap the right if. Here is a partial fix, where the programmer added braces only around the outer if:

C++ · editable

The output is:

Out of stock, cannot ship
end

The braces here wrap the outer if (paid), but the inner if (stock > 0) has no braces, so the else inside the outer block still pairs with the inner if. The first block prints the message because paid is true and stock is 0. The second block prints nothing, because paid is false and the whole block is skipped. The indentation suggests a different pairing, and the output does not match it.

Common mistakes

A second line without braces

This is the most common mistake in code that started simple. An if has one statement under it. Later, the author adds a line below it and forgets that the line is outside the if.

C++ · editable

The output is:

Count is positive
Processing 3 items
Processing -1 items

The second line is not inside the if. It runs every time, so the program reports "Processing -1 items" even though the count is negative. The compiler warns about it with -Wall:

main.cpp:6:5: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
    6 |     if (count > 0)
      |     ^~
main.cpp:8:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
    8 |         cout << "Processing " << count << " items" << endl;
      |         ^~~~

The warning is followed by a note that names the line that is not guarded. Read the note, then add braces.

A stray semicolon after the condition

A semicolon ends a statement. If you place one right after the condition of an if, the body of the if becomes an empty statement, and the next line is no longer guarded:

C++ · editable

The output is:

Starting
done

The block with "Starting" is not the body of the if. It runs even though ready is false. The compiler reports the same kind of warning, -Wmisleading-indentation, because the indentation of the block suggests it is guarded. Remove the semicolon.

An else after a semicolon

If a stray semicolon sits between an if and its else, the if is already finished when the else arrives, so there is nothing for the else to pair with. This gives a compile error. The program does not run at all.

#include <iostream>
using namespace std;
 
int main() {
    int x = 5;
    if (x > 0);
        cout << "positive" << endl;
    else
        cout << "not positive" << endl;
}

The compiler stops with this message:

main.cpp:8:5: error: ‘else’ without a previous ‘if’
    8 |     else
      |     ^~~~

Here the message is clear, and the fix is to remove the semicolon after if (x > 0).

A missing closing brace

Each { needs a matching }. If one is missing, the compiler reads to the end of the file looking for it:

#include <iostream>
using namespace std;
 
int main() {
    int x = 5;
    if (x > 0) {
        cout << "positive" << endl;
    cout << "checked" << endl;
}

The compiler reports:

main.cpp:9:2: error: expected ‘}’ at end of input
    9 | }
      |  ^
main.cpp:4:12: note: to match this ‘{’
    4 | int main() {
      |            ^

The note points at the { that has no partner. Count the braces on each line from that point, and look for the one that is missing.

Braces around the wrong if

Braces help only when they close the inner if before the else. The partial fix in the section above shows this mistake, and it does not produce a compile error, so it is easy to miss. When you add braces, check which if they belong to.

Practice

Exercise 1

Predict the output of this function for a equal to 5, a equal to -1 and a equal to 20. The indentation is the same as in the examples above.

void show(int a) {
    cout << "a=" << a << ": ";
    if (a > 0)
        if (a > 10)
            cout << "big";
        else
            cout << "small";
    cout << endl;
}

Show the answer

For a equal to 5, the output is a=5: small. The outer test is true, the inner test is false, and the else belongs to the inner if. For a equal to -1, the output is a=-1: with nothing after the colon, because the outer test is false and the inner else is skipped. For a equal to 20, the output is a=20: big.

Exercise 2

Predict the output of this code:

int x = 3, y = 0;
if (x > 1)
    if (y > 0)
        cout << "p";
else
    cout << "q";
cout << "r" << endl;

Show the answer

The output is qr followed by a line break. The else belongs to if (y > 0), because that is the nearest if with no else. The test y > 0 is false, so the else prints q. The outer if has no else, so nothing else prints before the r.

Exercise 3

Add braces to the code in exercise 2 so that the else belongs to if (x > 1). Then predict the output.

Show the answer

int x = 3, y = 0;
if (x > 1) {
    if (y > 0)
        cout << "p";
} else {
    cout << "q";
}
cout << "r" << endl;

The output is r followed by a line break. The test x > 1 is true, so the outer body runs. Its inner if is false and prints nothing. The else is skipped because the outer test was true.

Exercise 4

This code has one missing set of braces. Say which lines run when count is 0, and explain why.

int count = 0;
if (count > 0)
    cout << "first" << endl;
    cout << "second" << endl;

Show the answer

Only second prints. The first cout is the body of the if, and the test is false, so it is skipped. The second cout is not part of the if, because there are no braces. It always runs. The fix is to wrap both lines in braces if both are meant to depend on the test.

Summary

  • An else pairs with the nearest earlier if that does not have an else yet. Indentation does not change this.
  • A nested if followed by an else is the usual source of the bug, and the output can look right for some inputs and wrong for others.
  • Put braces around the body of every if and every else, so the owner of each else is written in the code.
  • Read -Wdangling-else and -Wmisleading-indentation warnings as bugs to fix, not as noise.
  • A stray semicolon after an if removes its body. A missing brace stops the compiler at the end of the file.
Practice this topic: coding problems for C++ Conditionals →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Decision tables