Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 1 of 6

A program that always does the same thing isn't much of a program. if is how it chooses.

#include <iostream>

int main() {
    int temperature = 32;

    if (temperature > 30) {
        std::cout << "Hot\n";
    } else if (temperature > 20) {
        std::cout << "Warm\n";
    } else {
        std::cout << "Cool\n";
    }
    return 0;
}
Hot

The condition in parentheses must produce a bool. Conditions are checked top to bottom and the first true branch wins — the rest are skipped entirely, even if they'd also be true.

Comparison operators

| Operator | Meaning | | --- | --- | | == | equal to | | != | not equal to | | < > | less / greater than | | <= >= | less / greater than or equal |

== compares. = assigns. Confusing them is the classic C++ bug, and lesson 3.7 showed how const helps the compiler catch it.

Logical operators

#include <iostream>

int main() {
    int age = 25;
    bool has_licence = true;

    if (age >= 18 && has_licence) {
        std::cout << "May drive\n";
    }
    if (age < 13 || age > 65) {
        std::cout << "Discounted ticket\n";
    } else {
        std::cout << "Full price\n";
    }
    if (!has_licence) {
        std::cout << "Needs a licence\n";
    }
    return 0;
}
May drive
Full price

&& is and, || is or, ! is not.

Both && and || short-circuit: they stop as soon as the answer is decided. In a && b, if a is false, b is never evaluated. That's not just an optimization — it's a safety mechanism you'll rely on constantly:

if (index < size && data[index] > 0) { /* ... */ }

If index is out of range the first test fails and data[index] — which would read invalid memory — never runs. Order matters: swap those two and the program breaks.

Truthiness

Any number can be used as a condition. Zero is false; everything else is true:

#include <iostream>

int main() {
    int count = 3;
    int zero = 0;

    if (count) {
        std::cout << "count is nonzero\n";
    }
    if (!zero) {
        std::cout << "zero is falsy\n";
    }
    return 0;
}
count is nonzero
zero is falsy

This is legal and common, but if (count > 0) says what you mean more clearly. Prefer the explicit comparison unless the value is genuinely a flag.

The dangling-else trap

Here's the bug the lesson title promises. Braces are optional around a single statement:

if (x > 0)
    std::cout << "positive\n";

That works. The trouble starts when someone adds a second line later:

if (x > 0)
    std::cout << "positive\n";
    std::cout << "definitely positive\n";   // NOT part of the if!

The indentation says both lines are conditional. The compiler disagrees — only the first statement belongs to the if. The second runs unconditionally, and the code looks correct.

The name "dangling else" comes from the nastier version, where an else attaches to a different if than the indentation suggests:

#include <iostream>

int main() {
    int x = 5;
    int y = -1;

    if (x > 0)
        if (y > 0)
            std::cout << "both positive\n";
        else
            std::cout << "surprise\n";

    return 0;
}
surprise

Read the indentation and you'd expect the else to pair with if (x > 0), so nothing should print. It doesn't: else always binds to the nearest unmatched if, which is if (y > 0).

The fix is a habit, not a rule

Always use braces, even for one line.

#include <iostream>

int main() {
    int x = 5;
    int y = -1;

    if (x > 0) {
        if (y > 0) {
            std::cout << "both positive\n";
        }
    } else {
        std::cout << "x is not positive\n";
    }

    std::cout << "done\n";
    return 0;
}
done

Now the structure is unambiguous to both the compiler and the reader, and adding a line later can't silently change the meaning. Every major C++ style guide says the same thing. It costs two characters.

Initializers in if (C++17)

You can declare a variable scoped to the if itself:

#include <iostream>
#include <string>

int main() {
    std::string text = "evil genius society";

    if (std::size_t pos = text.find("genius"); pos != std::string::npos) {
        std::cout << "found at " << pos << "\n";
    }
    // pos does not exist here

    return 0;
}
found at 5

The variable exists only inside the if and its else. That keeps the surrounding scope clean and stops you accidentally using a search result you never checked. Lesson 4.6 covers scope properly.

Try it

Write a program that classifies a score out of 100 into a letter grade: 90+ is A, 80+ is B, 70+ is C, 60+ is D, below that is F. Test it with 85.

Checkpoint

  • if / else if / else checks conditions in order; the first true branch wins.
  • && and || short-circuit, which lets you guard an unsafe operation with a check before it.
  • Zero is falsy and nonzero is truthy, but explicit comparisons read better.
  • Always use braces, even around a single statement — this prevents both the added-line bug and the dangling-else bug.
  • else binds to the nearest unmatched if, regardless of how the code is indented.
  • C++17 lets you declare a variable inside the if that's scoped to that statement.

Questions about this lesson

Log in to ask a question.