Conditionals in C — exercises with grades, triangles, a login without loops and bus fares (and the 1.10 € balance that was not 1.10 €)

To practise conditionals I built a simulator of my bus card: you loaded a balance, picked a trip and it told you whether you had enough. I wanted a special message for when the balance was exactly the ticket price, 1.10 €. I tried it with 1.10 € on the card and the program told me it did not match. A number that is not equal to itself. I spent half an afternoon convinced gcc was broken, until I printed the balance with ten decimals. That program is snippet 3 in “What’s wrong here?”, and the lesson it left (you do not compare a float with ==) comes back in the solutions.

You have seen the C conditionals theory and written the three programs in the practice. Now it is your turn: four exercises on three levels, three snippets with a hidden bug, solutions commented line by line and a cheat sheet to revise before the exam. Every program reads with scanf and every output you will see comes from a real run, with what was typed after each question.

How to use this article

  1. Read the statement and the expected output. Write your solution in gedit without looking at the solution.
  2. Compile with gcc -Wall -Wextra exercise.c -o exercise and do not move on until there are no warnings.
  3. Run it with the same data as the statement and compare your output line by line. If you are stuck for more than 10 minutes, open the hint.
  4. Compare with the commented solution: even if your program works, read the “Why it is done this way”.

Basic level

Exercise 1: grade converter between systems

Write a program that asks for a grade on the Spanish scale (0-10) and converts it to the US letter grade and to the 0 to 100 scale. If the grade is out of range or not a number, the program complains and ends with return 1. The equivalences are:

Spanish gradeLetter0-100 scale
9 to 10A90 to 100
7 to 8.9B70 to 89
5 to 6.9C50 to 69
3 to 4.9D30 to 49
0 to 2.9F0 to 29

Expected output with a 7.5:

Spanish grade (0-10): 7.5

--- Conversions ---
Spanish scale:      7.50 / 10
US scale:           75.00 / 100
Letter grade:       B
Status:             Pass

An if / else if chain from the highest grade to the lowest for the letter, with braces in every branch. The 0 to 100 scale needs no if: it is a multiplication. And the “Status” fits in a ternary.

Exercise 2: triangle classifier by sides

Write a program that asks for the three sides of a triangle (whole numbers) and classifies it. First check whether the sides form a triangle (each side must be smaller than the sum of the other two); if not, complain and stop. Then classify it:

  • By sides: equilateral (all three equal), isosceles (exactly two equal) or scalene (all three different).
  • By angles, with Pythagoras and c being the largest side: right-angled if c² = a² + b², obtuse if c² > a² + b² and acute if c² < a² + b².

Expected output with 3, 4 and 5:

Side A: 3
Side B: 4
Side C: 5

--- Classification ---
Is it a valid triangle? Yes
Type by sides:    Scalene
Type by angles:   Right-angled

Validity is three conditions joined with &&. For Pythagoras you need to know which side is the largest: an if / else if that finds it and, at the same time, adds up the squares of the other two. Use int for the sides so == compares without rounding problems.

Intermediate level

Exercise 3: login system with limited attempts

Simulate a login with three attempts at most, still without loops. The user is “sergio” and, to keep strings out of it (they arrive with pointers), you only ask for the initial (a char) and a PIN (an int). There are three valid PINs, and each one gives a different access level, chosen with a switch:

PINAccess level
1234Administrator
5678User
9999Guest

If an attempt fails, it moves on to the next; if all three fail, the account is locked. Expected output when the third attempt succeeds (typing x and 1234, s and 1111, s and 5678):

=== LOGIN SYSTEM ===

Attempt 1 of 3
User initial: x
PIN: 1234
✗ Wrong user or PIN

Attempt 2 of 3
User initial: s
PIN: 1111
✗ Wrong user or PIN

Attempt 3 of 3
User initial: s
PIN: 5678

✓ Access granted. Welcome, sergio.
Access level: User

And when all three fail:

=== LOGIN SYSTEM ===

Attempt 1 of 3
User initial: s
PIN: 1
✗ Wrong user or PIN

Attempt 2 of 3
User initial: s
PIN: 2
✗ Wrong user or PIN

Attempt 3 of 3
User initial: a
PIN: 1234
✗ Wrong user or PIN

Account locked: 3 failed attempts

A variable int logged_in = 0; that becomes 1 when an attempt is correct. Each new attempt goes inside an if (!logged_in). To read the initial, scanf(" %c", &initial): the space before %c is mandatory.

Final challenge

Exercise 4: transport fare calculator

Work out the price of a bus ticket (made-up fares) from the age, the youth card, the zones and the departure hour:

  • Base price: 1.50 € for 1 to 3 zones, 2.50 € for 4 to 6 and 3.50 € from 7 on.
  • Discount on the base price: under 4, free (100 %); 4 to 12, 50 %; 13 to 25 with a youth card, 30 %; 65 and over, 40 %; everyone else, no discount.
  • Rush-hour surcharge (7 to 9 and 17 to 19, both included): +20 % on the already discounted price.

If the zones are below 1 or the hour is not between 0 and 23, the program complains and stops. Expected output for a 22-year-old with a youth card, 4 zones, at 8:

=== BUS FARES ===
Age: 22
Do you have a youth card? (1 = yes, 0 = no): 1
Number of zones: 4
Departure hour (0-23): 8

--- Breakdown ---
Base price (4 zones): 2.50 €
Youth card discount: -0.75 € (30 %)
Subtotal: 1.75 €
Rush-hour surcharge: +0.35 € (20 %)
TOTAL: 2.10 €

Split the problem into four steps: base price (if / else if by zones), which discount applies (if / else if by age, storing a kind number), how much it is (a switch on that number) and surcharge (a condition with ||). Careful with the order of the ages: a 10-year-old with a youth card pays as a child.

What’s wrong here?

Three short snippets that compile and run, but do not do what they seem to. Read each one, predict the output and then read the explanation.

Snippet 1: the initial in double quotes

#include <stdio.h>

int main(void) {
    char initial = 's';              // as if the user had typed an s
    int pin = 1234;

    if (initial == "s" && pin == 1234) {
        printf("✓ Access granted\n");
    } else {
        printf("✗ Wrong user or PIN\n");
    }
    return 0;
}

What do you think it prints, if the initial is an s and the PIN is 1234?

Real output:

✗ Wrong user or PIN

Access denied with the right data. 's' (single quotes) is a char: number 115 in the ASCII table. "s" (double quotes) is a string, and in a comparison C uses the memory address where it is stored. Comparing a small number with a memory address never gives 1. gcc warns twice:

login_quotes.c: In function 'main':
login_quotes.c:7: warning: comparison between pointer and integer
login_quotes.c:7: warning: comparison with string literal results in unspecified behavior [-Waddress]

Using double quotes for a character. In C, 's' is a char and "s" is a string: they are not interchangeable as in Python.

The fix is to change the quotes, initial == 's', and you get what you expected:

✓ Access granted

If a gcc warning says “comparison between pointer and integer”, it is almost always this: you compared a char with something in double quotes.

Snippet 2: the child with his sister’s youth card

#include <stdio.h>

int main(void) {
    int age = 10;
    int youth_card = 1;           // a child with his sister's youth card
    int percent;

    if (age <= 25 && youth_card) {
        percent = 30;
    } else if (age <= 12) {
        percent = 50;
    } else {
        percent = 0;
    }
    printf("Discount: %d %%\n", percent);
    return 0;
}

A 10-year-old should get the 50 % child discount. What does it print?

Real output:

Discount: 30 %

30 %. The child meets age <= 25 && youth_card (10 ≤ 25 and carries a card), and since that is the first true branch, the chain ends there: else if (age <= 12) is not even looked at. There is no compiler warning, because the program is correct as far as C is concerned; the bug is in the logic.

Putting the broadest condition first when two conditions overlap. The “up to 12” range sits inside “up to 25”, so it must be asked first.

With the branches in the right order:

#include <stdio.h>

int main(void) {
    int age = 10;
    int youth_card = 1;
    int percent;

    if (age <= 12) {                // the most specific case first
        percent = 50;
    } else if (age <= 25 && youth_card) {
        percent = 30;
    } else {
        percent = 0;
    }
    printf("Discount: %d %%\n", percent);
    return 0;
}

Output:

Discount: 50 %

To find this kind of bug, test the edge cases: someone who meets two conditions at once (10 years old with a card), someone right on the limit (12, 13, 25, 65).

Snippet 3: the 1.10 € balance that is not 1.10 €

#include <stdio.h>

int main(void) {
    float balance = 1.10f;             // what is left on the card

    if (balance == 1.10) {             // the ticket costs 1.10
        printf("Exact balance: you end up at zero\n");
    } else {
        printf("No match: the balance is %.10f\n", balance);
    }
    return 0;
}

“Exact balance” or “No match”?

Real output:

No match: the balance is 1.1000000238

It is the program from the start. 1.10 cannot be stored exactly in binary (just as 1/3 cannot be written exactly in decimal), so the computer stores the closest number it can. The problem is that balance is a float and 1.10 without an f is a double, and each type ends up with a different “closest”. When comparing, C converts the float into a double and the two numbers are no longer equal:

1.10 as float, as double and in cents Zoom of the number line around 1.1: the double 1.1 is 1.100000000000000088 and the float 1.10f is 1.10000002384; they are different points, so balance == 1.10 gives 0. In cents, 110 == 110 is exact. Why is 1.10 not equal to 1.10? A huge zoom of the number line around 1.1: float and double do not store the same number 1.0999999 1.1000000 1.1000001 1.10 (double) 1.100000000000000088… balance (float 1.10f) 1.10000002384… balance == 1.10 → 0 int balance_cents = 110; 110 == 110 → 1
The float ends up further from the real 1.1 than the double. With the money in cents (int) the comparison is exact.

Comparing with == a float or double that comes from a number with decimals. It may work with some values and fail with others.

The cleanest fix for money is not to use decimals at all: store it in cents, in an int.

#include <stdio.h>

int main(void) {
    int balance_cents = 110;            // money in cents: integers are exact

    if (balance_cents == 110) {
        printf("Exact balance: you end up at zero\n");
    } else {
        printf("No match\n");
    }
    return 0;
}

Output:

Exact balance: you end up at zero

If you have to compare decimals, compare with a tolerance: if (balance - 1.10 < 0.001 && balance - 1.10 > -0.001). And if it is money, cents in an int.

Commented solutions

Solution to exercise 1

#include <stdio.h>

int main(void) {
    float grade;
    char letter;
    float us_grade;

    printf("Spanish grade (0-10): ");
    // One if for both failures: if scanf fails, the || does not look at the range
    if (scanf("%f", &grade) != 1 || grade < 0 || grade > 10) {
        printf("Error: grade out of range\n");
        return 1;
    }

    // Letter: from the highest to the lowest, each branch with its braces
    if (grade >= 9.0f) {
        letter = 'A';
    } else if (grade >= 7.0f) {
        letter = 'B';
    } else if (grade >= 5.0f) {
        letter = 'C';
    } else if (grade >= 3.0f) {
        letter = 'D';
    } else {
        letter = 'F';
    }

    us_grade = grade * 10;      // 0-10 to 0-100: just multiply

    printf("\n--- Conversions ---\n");
    printf("Spanish scale:      %.2f / 10\n", grade);
    printf("US scale:           %.2f / 100\n", us_grade);
    printf("Letter grade:       %c\n", letter);
    printf("Status:             %s\n", (grade >= 5.0f) ? "Pass" : "Fail");
    return 0;
}

Why it is done this way:

  • One if for both failures. scanf(...) != 1 || grade < 0 || grade > 10: if scanf reads nothing, the || is already true and grade, which would have no value, is not looked at (short-circuit).
  • The letter chain goes from A to F. With 7.5: 7.5 >= 9 no, 7.5 >= 7 yes → 'B', and it asks nothing more. If you started with grade >= 3, everything except a very low fail would come out as “D”.
  • The 0 to 100 scale has no if. 7.5 × 10 = 75.00. Writing five branches for that would just repeat the chain.
  • The status goes in a ternary because it only picks between two texts for the same %s.
  • The labels have spaces to line up the values in column 21; that is why “Status:” has so many spaces after it.

With a 4.9 (another run): 4.9 >= 5 is false and 4.9 >= 3 is true, so D, and the ternary says “Fail”.

Spanish grade (0-10): 4.9

--- Conversions ---
Spanish scale:      4.90 / 10
US scale:           49.00 / 100
Letter grade:       D
Status:             Fail

Solution to exercise 2

#include <stdio.h>

int main(void) {
    int a, b, c;                     // integer sides: so == compares without rounding errors
    int largest, sum_squares, largest_squared;

    printf("Side A: ");
    scanf("%d", &a);
    printf("Side B: ");
    scanf("%d", &b);
    printf("Side C: ");
    scanf("%d", &c);

    // Triangle inequality: each side smaller than the sum of the other two.
    // With a strict > it also rules out sides that are 0 or negative
    int valid = (a + b > c) && (a + c > b) && (b + c > a);

    printf("\n--- Classification ---\n");
    printf("Is it a valid triangle? %s\n", valid ? "Yes" : "No");
    if (!valid) {
        printf("Cannot classify\n");
        return 1;
    }

    // By sides: the strictest case first (all three equal)
    if (a == b && b == c) {
        printf("Type by sides:    Equilateral\n");
    } else if (a == b || b == c || a == c) {
        printf("Type by sides:    Isosceles\n");
    } else {
        printf("Type by sides:    Scalene\n");
    }

    // Pythagoras needs to know which side is the largest
    if (a >= b && a >= c) {
        largest = a;
        sum_squares = b * b + c * c;
    } else if (b >= a && b >= c) {
        largest = b;
        sum_squares = a * a + c * c;
    } else {
        largest = c;
        sum_squares = a * a + b * b;
    }
    largest_squared = largest * largest;

    if (largest_squared == sum_squares) {
        printf("Type by angles:   Right-angled\n");
    } else if (largest_squared > sum_squares) {
        printf("Type by angles:   Obtuse\n");
    } else {
        printf("Type by angles:   Acute\n");
    }
    return 0;
}

Why it is done this way:

  • int and not float for the sides. Pythagoras compares with ==, and with integers 5 × 5 = 25 and 3 × 3 + 4 × 4 = 25 are exactly equal. With decimals, snippet 3 has already shown what can happen.
  • Validity is stored in a variable (valid) because it is used twice: in the ternary of the printf and in if (!valid).
  • The strict > also rules out sides that are 0 or negative. With 0, 3 and 3: 0 + 3 > 3 is false. No separate if is needed.
  • Equilateral first. A triangle with all three sides equal also meets a == b || …: if isosceles came first, an equilateral would come out as “isosceles”.

The trickiest step is finding the largest side and, at the same time, adding up the squares of the other two:

// Pythagoras needs to know which side is the largest
if (a >= b && a >= c) {
    largest = a;
    sum_squares = b * b + c * c;
} else if (b >= a && b >= c) {
    largest = b;
    sum_squares = a * a + c * c;
} else {
    largest = c;
    sum_squares = a * a + b * b;
}
largest_squared = largest * largest;

With 3, 4 and 5: 3 >= 4 fails, so the first branch is skipped; 4 >= 3 && 4 >= 5 fails; it reaches the else: largest = 5 and sum = 3 × 3 + 4 × 4 = 9 + 16 = 25. Since 5 × 5 = 25, right-angled. Two more runs, with 2, 2 and 3 and with 1, 2 and 5:

Side A: 2
Side B: 2
Side C: 3

--- Classification ---
Is it a valid triangle? Yes
Type by sides:    Isosceles
Type by angles:   Obtuse

With 2, 2 and 3: two equal sides, isosceles; the largest is 3, 3 × 3 = 9 and 2 × 2 + 2 × 2 = 8; since 9 > 8, obtuse.

Side A: 1
Side B: 2
Side C: 5

--- Classification ---
Is it a valid triangle? No
Cannot classify

With 1, 2 and 5: 1 + 2 = 3, which is not greater than 5. valid is 0 and the program stops before classifying.

Solution to exercise 3

#include <stdio.h>

int main(void) {
    char initial;
    int pin;
    int logged_in = 0;               // 0 = not logged in yet; 1 = correct login

    printf("=== LOGIN SYSTEM ===\n");

    // Attempt 1
    printf("\nAttempt 1 of 3\n");
    printf("User initial: ");
    scanf(" %c", &initial);          // the space before %c skips the pending Enter
    printf("PIN: ");
    scanf("%d", &pin);
    if (initial == 's' && (pin == 1234 || pin == 5678 || pin == 9999)) {
        logged_in = 1;
    } else {
        printf("✗ Wrong user or PIN\n");
    }

    // Attempt 2: only if the first one failed
    if (!logged_in) {
        printf("\nAttempt 2 of 3\n");
        printf("User initial: ");
        scanf(" %c", &initial);
        printf("PIN: ");
        scanf("%d", &pin);
        if (initial == 's' && (pin == 1234 || pin == 5678 || pin == 9999)) {
            logged_in = 1;
        } else {
            printf("✗ Wrong user or PIN\n");
        }
    }

    // Attempt 3: the same block again (in the loops topic we will write it only once)
    if (!logged_in) {
        printf("\nAttempt 3 of 3\n");
        printf("User initial: ");
        scanf(" %c", &initial);
        printf("PIN: ");
        scanf("%d", &pin);
        if (initial == 's' && (pin == 1234 || pin == 5678 || pin == 9999)) {
            logged_in = 1;
        } else {
            printf("✗ Wrong user or PIN\n");
        }
    }

    if (logged_in) {
        printf("\n✓ Access granted. Welcome, sergio.\n");
        printf("Access level: ");
        switch (pin) {               // pin is an exact number: a job for switch
            case 1234:
                printf("Administrator\n");
                break;
            case 5678:
                printf("User\n");
                break;
            case 9999:
                printf("Guest\n");
                break;
        }
    } else {
        printf("\nAccount locked: 3 failed attempts\n");
        return 1;
    }
    return 0;
}

Why it is done this way:

  • logged_in works as a switch. It starts at 0 and only a correct attempt sets it to 1. Each new attempt sits inside if (!logged_in): once logged in, the rest do not run.
  • The brackets in (pin == 1234 || pin == 5678 || pin == 9999) are essential. Without them, initial == 's' && pin == 1234 || pin == 5678 || … is read as (initial == 's' && pin == 1234) || pin == 5678 || …, because && goes before ||: initial x with PIN 5678 would get in.
  • The switch on pin needs no default because it is only reached with one of the three valid PINs. Adding one does no harm, though.
  • The block is repeated three times. It is ugly on purpose: it is exactly what loops solve. In Loops in C we will write it only once.

Walkthrough of the run that succeeds at the third attempt:

AttemptInitialPINinitial == 's'Valid PIN?logged_in afterwards
1x1234010
2s1111100
3s5678111

At the end, logged_in is 1 and the switch jumps to case 5678: “User”.

Reading the initial with scanf("%c", &initial) without the space. In the second attempt it would read the Enter left behind by the previous PIN and the initial would be a newline.

Solution to exercise 4

#include <stdio.h>

int main(void) {
    int age, youth_card, zones, hour;
    int kind;                        // 0 normal, 1 under 4, 2 child, 3 youth, 4 senior
    double base_price, percent, discount, subtotal, surcharge, total;

    printf("=== BUS FARES ===\n");
    printf("Age: ");
    scanf("%d", &age);
    printf("Do you have a youth card? (1 = yes, 0 = no): ");
    scanf("%d", &youth_card);
    printf("Number of zones: ");
    scanf("%d", &zones);
    printf("Departure hour (0-23): ");
    scanf("%d", &hour);

    if (zones < 1 || hour < 0 || hour > 23) {
        printf("Error: data out of range\n");
        return 1;
    }

    // 1) Base price by zones
    if (zones <= 3) {
        base_price = 1.50;
    } else if (zones <= 6) {
        base_price = 2.50;
    } else {
        base_price = 3.50;
    }

    // 2) Which discount applies. Order matters: a 10-year-old with a youth card is a child
    if (age < 4) {
        kind = 1;
    } else if (age <= 12) {
        kind = 2;
    } else if (age <= 25 && youth_card) {
        kind = 3;
    } else if (age >= 65) {
        kind = 4;
    } else {
        kind = 0;
    }

    printf("\n--- Breakdown ---\n");
    printf("Base price (%d zones): %.2f €\n", zones, base_price);

    // 3) kind is an exact number: switch picks the percentage and the text at once
    switch (kind) {
        case 1:
            percent = 100;
            printf("Under-4 discount: ");
            break;
        case 2:
            percent = 50;
            printf("Child discount: ");
            break;
        case 3:
            percent = 30;
            printf("Youth card discount: ");
            break;
        case 4:
            percent = 40;
            printf("Over-65 discount: ");
            break;
        default:
            percent = 0;
            printf("No discount: ");
            break;
    }
    discount = base_price * percent / 100;
    printf("-%.2f € (%.0f %%)\n", discount, percent);
    subtotal = base_price - discount;
    printf("Subtotal: %.2f €\n", subtotal);

    // 4) Rush-hour surcharge: 7 to 9 or 17 to 19, on the subtotal
    int rush_hour = (hour >= 7 && hour <= 9) || (hour >= 17 && hour <= 19);
    surcharge = rush_hour ? subtotal * 0.20 : 0;
    if (rush_hour) {
        printf("Rush-hour surcharge: +%.2f € (20 %%)\n", surcharge);
    }
    total = subtotal + surcharge;
    printf("TOTAL: %.2f €\n", total);
    return 0;
}

Why it is done this way:

  • Deciding and calculating are kept apart. The age chain only decides which discount applies and stores it in kind. Then the switch turns that number into a percentage and a text. That way, if the senior percentage changes, you touch a single line.
  • The order of the ages is the one from snippet 2: under 4 first, then up to 12 and only then the youth card.
  • double for the amounts and %.2f to show them with two decimals. Here we only do sums; no amount is compared with ==.
  • The surcharge is worked out on the subtotal, not on the base price, and the ternary leaves it at 0 outside rush hour.

Zooming in on the switch, which picks the percentage and the text at once:

// 3) kind is an exact number: switch picks the percentage and the text at once
switch (kind) {
    case 1:
        percent = 100;
        printf("Under-4 discount: ");
        break;
    case 2:
        percent = 50;
        printf("Child discount: ");
        break;
    case 3:
        percent = 30;
        printf("Youth card discount: ");
        break;
    case 4:
        percent = 40;
        printf("Over-65 discount: ");
        break;
    default:
        percent = 0;
        printf("No discount: ");
        break;
}
discount = base_price * percent / 100;
printf("-%.2f € (%.0f %%)\n", discount, percent);

Check by hand with a 22-year-old, card, 4 zones and 8 o’clock: base price 2.50 € (4 ≤ 6); 22 is not < 4 or ≤ 12, but 22 ≤ 25 with a card → kind 3 → 30 %; 2.50 × 30 / 100 = 0.75; subtotal 2.50 − 0.75 = 1.75; 8 is between 7 and 9 → surcharge 1.75 × 0.20 = 0.35; total 1.75 + 0.35 = 2.10 €.

Breakdown of the ticket for a 22-year-old with a youth card Waterfall bars: base price 2.50 €, youth discount -0.75 €, subtotal 1.75 €, rush-hour surcharge +0.35 € and total 2.10 €. The ticket step by step (22, youth card, 4 zones, 8:00) The discount applies to the base price; the surcharge, to the subtotal 2.50 € base -0.75 € youth 30 % 1.75 € subtotal +0.35 € rush 20 % 2.10 € TOTAL 2.50 × 30 / 100 = 0.75 · 1.75 × 0.20 = 0.35 · 1.75 + 0.35 = 2.10
If you applied the 20 % to the original 2.50 the surcharge would be 0.50 and the total 2.25: the order of the steps changes the price.

Two more runs. A 70-year-old woman, no card, 8 zones and at 18:

=== BUS FARES ===
Age: 70
Do you have a youth card? (1 = yes, 0 = no): 0
Number of zones: 8
Departure hour (0-23): 18

--- Breakdown ---
Base price (8 zones): 3.50 €
Over-65 discount: -1.40 € (40 %)
Subtotal: 2.10 €
Rush-hour surcharge: +0.42 € (20 %)
TOTAL: 2.52 €

3.50 × 40 / 100 = 1.40; 3.50 − 1.40 = 2.10; 2.10 × 0.20 = 0.42; total 2.52 €. And the 10-year-old with his sister’s youth card, 2 zones, at midday:

=== BUS FARES ===
Age: 10
Do you have a youth card? (1 = yes, 0 = no): 1
Number of zones: 2
Departure hour (0-23): 12

--- Breakdown ---
Base price (2 zones): 1.50 €
Child discount: -0.75 € (50 %)
Subtotal: 0.75 €
TOTAL: 0.75 €

The 50 % child discount comes out, not the 30 % youth one, and at 12 there is no surcharge, so the rush-hour line does not even appear.

Watch it step by step in Python Tutor

Python Tutor runs the code line by line and shows the value of each variable at every step. Choose C as the language and paste snippet 2 exactly as it is, bug included, to watch it live:

#include <stdio.h>

int main(void) {
    int age = 10;
    int youth_card = 1;
    int percent;

    if (age <= 25 && youth_card) {
        percent = 30;
    } else if (age <= 12) {
        percent = 50;
    } else {
        percent = 0;
    }
    printf("Discount: %d %%\n", percent);
    return 0;
}

Output:

Discount: 30 %

What to watch step by step:

  1. The variables panel shows age = 10 and youth_card = 1; percent has no value yet.
  2. When if (age <= 25 && youth_card) is evaluated, the arrow enters the first branch: both sides of the && are true.
  3. After percent = 30, the arrow jumps straight to the printf: the else if (age <= 12) lines never run.
  4. Swap the first two conditions and repeat: now the arrow goes in through age <= 12 and percent is 50.

Cheat sheet: conditionals in C

The essentials in 30 seconds: in C the condition is a number (0 = false, anything else = true) and a comparison returns 1 or 0. if / else if / else runs only the first true branch, so order matters. Always braces and never a ; after the if. == compares and = assigns. switch jumps to a case with an exact integer value and keeps going until the first break. Compile with -Wall -Wextra.

I want…CodeResult
Check a rangeif (age >= 13 && age <= 25)1 only between 13 and 25
One of two slots(h >= 7 && h <= 9) || (h >= 17 && h <= 19)1 at rush hour
Negateif (!logged_in)Gets in if logged_in is 0
Pick one of two valuesx = (cond) ? a : b;a if cond is not 0
Store a conditionint passed = (grade >= 5);1 or 0
Validate the inputif (scanf("%d", &n) != 1)Gets in if no number was typed
Divide safelyif (d != 0 && x / d > 2)Does not divide if d is 0
Several cases, same codecase 6: case 7: …; break;Fall-through on purpose
Compare a characterif (c == 's')Single quotes
Compare moneyif (cent == 110)Cents in an int: exact

Templates from the exercises:

ExercisePatternWith the statement’s data
1. ConverterChain of >= from high to low + ternary7.5 → B, 75.00 / 100, Pass
2. Trianglesvalid with &&, largest side with if / else if, Pythagoras with int3, 4, 5 → Scalene, Right-angled
3. Loginint logged_in = 0; + if (!logged_in) { … } per attempt + switch (pin)Third attempt, 5678 → User
4. FaresChain → kind, switch (kind) → percentage, surcharge with ||22, card, 4 zones, 8:00 → 2.10 €

Symptom and likely cause:

SymptomLikely cause
The if always gets in and the variable changes value= instead of ==
A line runs even though the condition is falseMissing braces, or a ; after the if
13 <= age <= 25 lets anyone throughYou need two comparisons with &&
A case never holds when it shouldChain order: a broader condition comes first
The switch does two thingsMissing break
switch quantity not an integerswitch on a float or double
comparison between pointer and integerchar compared with "s" instead of 's'
Two “equal” decimals give 0 with ==float / double rounding: tolerance or cents
Floating point exceptionDivision or modulo by 0 (check the (int) of the divisor)

Before you hand it in, ask yourself:

  1. Have I compiled with -Wall -Wextra and are there no warnings left?
  2. Do all branches of every if have braces, and is there no ; after the bracket of any if?
  3. Have I tested the edge cases (the exact limit value, someone who meets two conditions at once, invalid data)?
  4. Does every case end in break, and does the switch have a default?

With this, conditionals in C are under control. In the next topic, Loops in C, the three-attempt login will be written with a single block and the ticket machine will serve as many passengers as needed.

Keep goingAll C articles

Did this article help you? Here is how you can support SergioLearns →

Sigue aprendiendo

Leave a Reply

Your email address will not be published. Required fields are marked *