C Programming: Low-Level Mastery
Control Flow

If-Else Statements

Master conditional execution in C with if, else if, and else statements. Learn to make decisions in code, handle multiple conditions, nest statements, and avoid common pitfalls.

Understanding Conditional Execution

Conditional statements allow programs to make decisions and execute different code based on conditions. Without conditionals, programs would execute the same sequence every time - useful for calculators but not for interactive applications. The if statement is C's fundamental decision-making construct, enabling programs to respond to different inputs and situations.

In C, conditions are expressions that evaluate to true (any non-zero value) or false (zero). The if statement tests a condition and executes code only when the condition is true. This simple mechanism powers complex decision logic from simple value checks to sophisticated multi-branch decisions.

C
#include <stdio.h>

int main(void) {
    int age = 18;
    
    /* Basic if statement */
    if (age >= 18) {
        printf("You are an adult\n");
    }
    
    /* Condition can be any expression */
    if (age) {  // True if age != 0
        printf("Age is non-zero\n");
    }
    
    /* Condition with logical operators */
    if (age >= 18 && age < 65) {
        printf("Working age\n");
    }
    
    return 0;
}

The if Statement

The if statement has a simple syntax: the keyword if, followed by a condition in parentheses, then a statement or block to execute when true. If the condition is false, execution skips the statement and continues after it. Using braces { } to create a block allows multiple statements in the if body.

C
/* Single statement (braces optional but recommended) */
if (x &gt; 0)
    printf("Positive\n");

/* Better: Always use braces */
if (x &gt; 0) {
    printf("Positive\n");
}

/* Multiple statements require braces */
if (score >= 90) {
    printf("Grade: A\n");
    printf("Excellent work!\n");
    grade = 'A';
}

/* Common mistake: Missing braces */
if (x &gt; 0)
    printf("First line\n");
    printf("Second line\n");  // NOT part of if! Always executes

/* Correct version */
if (x &gt; 0) {
    printf("First line\n");
    printf("Second line\n");  // Now part of if block
}

/* Empty if statement (rare but valid) */
if (condition) {
    // TODO: Implement later
}

/* Complex conditions */
if ((x &gt; 0 && x < 100) || (y == 0)) {
    printf("Complex condition met\n");
}

/* Function calls in conditions */
if (isdigit(ch)) {
    printf("Character is a digit\n");
}

/* Assignment in condition (be careful!) */
int value;
if ((value = getValue()) != 0) {  // Assign AND test
    printf("Got non-zero value: %d\n", value);
}

The if-else Statement

The else clause provides an alternative execution path when the if condition is false. This creates binary decisions: do one thing or another. The else is always associated with the nearest previous if that doesn't already have an else, which matters when nesting if statements.

C
/* Basic if-else */
int age = 16;

if (age >= 18) {
    printf("Adult\n");
} else {
    printf("Minor\n");
}

/* Both branches can have multiple statements */
if (password_correct) {
    printf("Access granted\n");
    login_count++;
    last_login = time(NULL);
} else {
    printf("Access denied\n");
    failed_attempts++;
    log_failed_login();
}

/* Returning from both branches */
if (value < 0) {
    printf("Negative\n");
    return -1;
} else {
    printf("Non-negative\n");
    return 0;
}

/* Using else to handle default case */
if (operation == '+') {
    result = a + b;
} else if (operation == '-') {
    result = a - b;
} else if (operation == '*') {
    result = a * b;
} else if (operation == '/') {
    if (b != 0) {
        result = a / b;
    } else {
        printf("Division by zero!\n");
        return -1;
    }
} else {
    printf("Unknown operation\n");
    return -1;
}

The else-if Ladder

Multiple else-if clauses create a chain of conditions tested sequentially from top to bottom. The first condition that evaluates to true executes its block, then control jumps past all remaining else-if and else clauses. This structure handles multiple mutually exclusive conditions elegantly, like grade classification or menu selection.

C
/* Grade classification */
int score = 85;
char grade;

if (score >= 90) {
    grade = 'A';
    printf("Excellent!\n");
} else if (score >= 80) {
    grade = 'B';
    printf("Good job!\n");
} else if (score >= 70) {
    grade = 'C';
    printf("Fair\n");
} else if (score >= 60) {
    grade = 'D';
    printf("Passing\n");
} else {
    grade = 'F';
    printf("Failed\n");
}

printf("Grade: %c\n", grade);

/* Temperature classification */
int temp = 72;

if (temp < 32) {
    printf("Freezing\n");
} else if (temp < 50) {
    printf("Cold\n");
} else if (temp < 70) {
    printf("Cool\n");
} else if (temp < 85) {
    printf("Warm\n");
} else {
    printf("Hot\n");
}

/* Only first matching condition executes */
int x = 25;

if (x &gt; 0) {
    printf("Positive\n");  // This executes
} else if (x &gt; 10) {
    printf("Greater than 10\n");  // Skipped (even though true!)
} else if (x &gt; 20) {
    printf("Greater than 20\n");  // Skipped (even though true!)
}

/* Order matters! */
if (x &gt; 20) {
    printf("Greater than 20\n");  // Most specific first
} else if (x &gt; 10) {
    printf("Greater than 10\n");
} else if (x &gt; 0) {
    printf("Positive\n");
}

Nested if Statements

You can place if statements inside other if statements, creating nested conditions. This allows complex decision trees where inner conditions are only checked when outer conditions are true. While powerful, deep nesting reduces readability - consider refactoring deeply nested code into separate functions or using logical operators to flatten the structure.

C
/* Basic nesting */
int age = 25;
int has_license = 1;

if (age >= 18) {
    if (has_license) {
        printf("You can drive\n");
    } else {
        printf("Get a license first\n");
    }
} else {
    printf("Too young to drive\n");
}

/* Multiple levels of nesting */
if (user_logged_in) {
    if (user_is_admin) {
        if (action_is_delete) {
            if (confirm_deletion) {
                printf("Deleting...\n");
            } else {
                printf("Deletion cancelled\n");
            }
        }
    } else {
        printf("Admin access required\n");
    }
} else {
    printf("Please log in\n");
}

/* Better: Flatten with early returns */
if (!user_logged_in) {
    printf("Please log in\n");
    return;
}

if (!user_is_admin) {
    printf("Admin access required\n");
    return;
}

if (!action_is_delete || !confirm_deletion) {
    printf("Deletion cancelled\n");
    return;
}

printf("Deleting...\n");  // Much clearer!

/* Nested with else-if */
if (category == 'E') {
    if (years_experience &gt; 5) {
        salary = 80000;
    } else if (years_experience &gt; 2) {
        salary = 60000;
    } else {
        salary = 45000;
    }
} else if (category == 'M') {
    if (years_experience &gt; 5) {
        salary = 120000;
    } else {
        salary = 90000;
    }
} else {
    salary = 35000;
    }
}

Common Pitfalls and Best Practices

If statements are straightforward but have subtle gotchas. Understanding these common mistakes helps you write correct, maintainable conditional logic. Many bugs come from incorrect condition logic, missing braces, or unintended else association.

C
/* Pitfall 1: Assignment instead of comparison */
int x = 5;

if (x = 10) {  // WRONG: Assignment, not comparison!
    printf("Always executes\n");  // x is now 10 (non-zero = true)
}

/* Correct */
if (x == 10) {
    printf("x equals 10\n");
}

/* Prevent this mistake: Put constant first (Yoda conditions) */
if (10 == x) {  // if (10 = x) would be compile error
    printf("x equals 10\n");
}

/* Pitfall 2: Missing braces causing subtle bugs */
if (condition)
    do_something();
    do_another();  // Always executes! Not part of if

/* Correct */
if (condition) {
    do_something();
    do_another();
}

/* Pitfall 3: Dangling else */
if (x &gt; 0)
    if (y &gt; 0)
        printf("Both positive\n");
else  // Which if does this else belong to?
    printf("Ambiguous!\n");

/* Else associates with nearest if (y &gt; 0), not (x &gt; 0)! */

/* Correct with braces */
if (x &gt; 0) {
    if (y &gt; 0) {
        printf("Both positive\n");
    }
} else {
    printf("x not positive\n");
}

/* Pitfall 4: Comparing floating-point for equality */
double a = 0.1 + 0.2;
if (a == 0.3) {  // May be false due to rounding!
    printf("Equal\n");
}

/* Correct: Use epsilon comparison */
#define EPSILON 0.00001
if (fabs(a - 0.3) < EPSILON) {
    printf("Approximately equal\n");
}

/* Pitfall 5: Empty statement (semicolon) */
if (x &gt; 0);  // Semicolon creates empty statement!
{
    printf("Always executes\n");  // Not part of if!
}

/* Pitfall 6: Wrong logical operator */
if (x &gt; 0 && x < 10) {"{ }"}  // Both must be true (correct)
if (x &gt; 0 || x < 10) {"{ }"}  // At least one true (always true!)

/* Best Practice 1: Always use braces */
// Even for single statements
if (condition) {
    statement();
}

/* Best Practice 2: Keep conditions simple */
// Bad:
if (((a &gt; 5 && b < 10) || c == 0) && !(d &gt; 20 || e < 5)) {"{ }"}

// Good:
int condition1 = (a &gt; 5 && b < 10) || (c == 0);
int condition2 = !(d &gt; 20 || e < 5);
if (condition1 && condition2) {"{ }"}

/* Best Practice 3: Handle edge cases first */
if (pointer == NULL) {
    return ERROR;
}
if (size == 0) {
    return EMPTY;
}
// Normal processing here

/* Best Practice 4: Avoid deep nesting */
// Use early returns or combine conditions
if (!valid) return;
if (!authorized) return;
// Process here (no nesting)

/* Best Practice 5: Use consistent comparison order */
if (MIN <= value && value <= MAX) {"{ }"}  // Clear range check

Ternary Operator (Conditional Expression)

The ternary operator (?:) is a compact alternative to simple if-else statements. Syntax: condition ? value_if_true : value_if_false. It's an expression that returns a value, making it useful for assignments and function arguments. Use sparingly - complex ternaries reduce readability.

C
/* Basic ternary usage */
int a = 10, b = 20;
int max = (a &gt; b) ? a : b;
printf("Max: %d\n", max);  // 20

/* Equivalent if-else */
int max2;
if (a &gt; b) {
    max2 = a;
} else {
    max2 = b;
}

/* In printf */
printf("Number is %s\n", (num >= 0) ? "positive" : "negative");

/* Nested ternary (discouraged - hard to read) */
char *grade = (score >= 90) ? "A" :
              (score >= 80) ? "B" :
              (score >= 70) ? "C" :
              (score >= 60) ? "D" : "F";

/* Better: Use if-else for this */
char *grade2;
if (score >= 90) grade2 = "A";
else if (score >= 80) grade2 = "B";
else if (score >= 70) grade2 = "C";
else if (score >= 60) grade2 = "D";
else grade2 = "F";

/* Common uses */
// Clamping values
int clamped = (value < 0) ? 0 : (value &gt; 100) ? 100 : value;

// Default values
int count = (user_input != 0) ? user_input : DEFAULT_COUNT;

// String selection
printf("%s\n", (is_online) ? "Online" : "Offline");

/* When to use */
// Good: Simple, clear choice
int abs_value = (x < 0) ? -x : x;

// Bad: Complex logic
int result = (a &gt; b) ? ((c &gt; d) ? e : f) : ((g &gt; h) ? i : j);  // Confusing!

Summary & What's Next

Key Takeaways:

  • ✅ if statement executes code when condition is true
  • ✅ else provides alternative path when condition is false
  • ✅ else-if chains test multiple conditions sequentially
  • ✅ Always use braces { } even for single statements
  • ✅ Watch for assignment (=) vs comparison (==) mistake
  • ✅ Avoid deep nesting - use early returns
  • ✅ Ternary operator (?:) for simple conditional expressions
  • ✅ Put most specific conditions first in else-if chains

What's Next?

Let's learn about loops - while, do-while, and for!