C Programming C++ Java JavaScript Kotlin PHP Python

πŸ’» C Programming

Learn the basics of C programming, including variables, loops, and functions.

CONTROL FLOW STATEMENTS::(if, if-else, nested if, switch-case)

Decision-making statements let your program choose different actions based on conditions. In C the primary decision statements are:

  • if

  • if-else

  • nested if

  • else if (ladder)

  • switch-case

All conditions are expressions evaluated as true (nonzero) or false (zero).


1. if β€” single-branch decision

Definition: Evaluate a condition; if it is true, execute a statement or block; otherwise skip it.

Syntax:

if (condition) statement; // single statement allowed (but see best practice)

or with a block:

if (condition) { statement1; statement2; }

Example:

int x = 5; if (x > 0) { printf("x is positive\n"); }

Flow (ASCII):

[ condition? ] --yes--> [ execute block ] --> [ continue ] --no --> [ skip block ] --> [ continue ]

Notes:

  • condition is any expression; zero means false, nonzero true.

  • Use braces {} even for single statements to avoid bugs when adding lines later.


2. if-else β€” two-way decision

Definition: If condition is true execute first block; otherwise execute the else block.

Syntax:

if (condition) { // true-branch } else { // false-branch }

Example (parity test):

int n; scanf("%d", &n); if (n % 2 == 0) { printf("even\n"); } else { printf("odd\n"); }

Flow (ASCII):

[ condition? ] --yes--> [ true branch ] --> [ continue ] --no --> [ false branch ] -> [ continue ]

Use-case: Choose between two mutually exclusive actions (e.g., valid vs invalid).


3. Nested if

Definition: Place an if (or if-else) inside another if or else to check additional conditions.

Example (age & permission):

int age = 18; int hasPermit = 0; if (age >= 18) { if (hasPermit) { printf("Allowed\n"); } else { printf("Need permit\n"); } } else { printf("Underage\n"); }

When to use: When decisions are hierarchical (outer condition filters candidates for deeper checks).

Pitfall: Deep nesting makes code hard to readβ€”refactor into separate functions or use early returns to flatten.


4. else if ladder β€” multiple mutually exclusive tests

Definition: Chain multiple conditions; the first true branch runs; remaining branches are skipped.

Syntax:

if (cond1) { // branch 1 } else if (cond2) { // branch 2 } else if (cond3) { // branch 3 } else { // fallback }

Example (grading):

int marks = 78; if (marks >= 80) { puts("A"); } else if (marks >= 65) { puts("B"); } else if (marks >= 50) { puts("C"); } else { puts("Fail"); }

Flow (ASCII simplified):

cond1? --yes-> branch1 -> end --no --> cond2? --yes-> branch2 -> end --no --> cond3? ...

Use-case: Ranges or ordered checks where only one branch should match (e.g., grading, status selection).

Best practice: Order conditions from most specific to least specific; ensure non-overlapping or intended priority.


5. switch-case β€” selecting based on discrete integral values

Definition: Multi-way branch that compares an integer expression against constant case labels. Useful when selecting among many discrete values.

Syntax:

switch (expr) { case CONST1: // statements break; case CONST2: // statements break; ... default: // fallback }

Example (menu):

int choice; printf("1.Add 2.Remove 3.Exit\n"); scanf("%d", &choice); switch (choice) { case 1: puts("Add selected"); break; case 2: puts("Remove selected"); break; case 3: puts("Exiting"); break; default: puts("Invalid choice"); }

Key rules & notes:

  • expr must be of an integral type (e.g., int, char, enum) or a type that promotes to integral. Floating-point is not allowed.

  • case labels must be compile-time constant integer expressions.

  • break prevents fall-through to the next case. Without break, execution continues into the next case (deliberate fall-through is allowed but should be commented).

  • default executes if no case matches; it's optional but recommended.

  • You can group labels:

switch (ch) { case 'a': case 'e': case 'i': case 'o': case 'u': puts("vowel"); break; default: puts("consonant"); }

Flow (ASCII):

compute expr -> compare with case1? -> if yes execute -> break -> end no -> compare with case2? -> ... no -> default -> end

Use-cases: Menus, opcode/command dispatch, state machines, multi-value selection where values are discrete.


6. Practical examples (combined patterns)

Input validation + branch example

char buf[64]; if (fgets(buf, sizeof buf, stdin)) { // remove \n buf[strcspn(buf, "\n")] = '\0'; if (strlen(buf) == 0) { puts("Empty input"); } else { puts("Processing..."); } }

Switch with enum (good style)

typedef enum { RED, GREEN, BLUE } Color; Color c = GREEN; switch (c) { case RED: puts("red"); break; case GREEN: puts("green"); break; case BLUE: puts("blue"); break; default: puts("unknown"); break; }

7. Common pitfalls & gotchas

  • Assignment vs comparison: if (x = 0) assigns 0 and tests false. Use == for comparison. Some prefer if (0 == x) to cause compile error if = used accidentally.

  • Missing braces: if (cond) statement; statement2; β€” the second statement is always executed. Always use {}.

  • Mixing scanf & newline handling: After scanf("%d", &n), a newline remainsβ€”be careful when using fgets() afterward.

  • Switch fall-through: Forgetting break causes accidental fall-through. If fall-through is intentional, comment /* fall through */.

  • switch works on integers only: Do not attempt to switch on strings; instead use if-else chains or map strings to integer codes.

  • Complex conditions unreadable: Long boolean expressions are error-prone; break into named boolean variables or functions.


8. Principles & best practices

  1. Use braces {} for every if, else, and case body β€” prevents bugs and improves clarity.

  2. Prefer early returns (guard clauses) to reduce nested if depth:

    if (!valid) return -1; // main logic here
  3. Order else if from most specific to least to ensure correct behavior.

  4. Prefer switch for many discrete values (cleaner and often faster than many else if branches).

  5. Use enum for meaningful case labels (improves readability and avoids magic numbers).

  6. Comment intentional fall-throughs in switch.

  7. Check user input and handle default/invalid cases robustly.

  8. Extract complex condition logic into well-named helper functions for readability and reuse.

  9. Don’t put heavy computation or side effects in condition expressions; evaluate beforehand if needed.

  10. Compile with warnings enabled (-Wall -Wextra) to catch suspicious if/switch code.


9. When to use which

  • Use if / if-else for simple true/false decisions or when testing ranges.

  • Use nested if when one condition depends on a previous condition being true.

  • Use else if ladder for ordered conditions or range tests (e.g., grading).

  • Use switch when you need to branch on many fixed discrete values (e.g., menu/options, protocol opcodes, enum states).


10. Quick checklist before committing decision code

  • Are conditions clear and simple?

  • Are braces present for all blocks?

  • Are early exits used to avoid deep nesting?

  • For switch: are case labels constant and break present? Is default handled?

  • Is input validated before use in conditions?

  • Do you avoid mixing side effects inside condition expressions?

❓

No quizzes yet

Quizzes to test your knowledge coming soon!

πŸ“š Recent Tutorials

Explore the latest tutorials added to our platform

Code copied to clipboard!