December Code

C control flow interview questions, with answers

Control flow questions in C are rarely about what a loop is. They are about the places where C is more permissive than the languages students meet later: any integer can be a condition, an assignment can hide inside an if, a switch keeps running into the next case, and goto is still part of the language. Interviewers use short snippets to check that you trace them exactly, so each answer below comes with code whose output was produced by compiling and running it.

The questions cover switch, the three loops, jumping out of nested loops, goto, truth values and the conditional operator. Then take the free C diagnostic — ten questions across every C topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.How does switch fall through in C, and when is fall-through useful?

    In short: After jumping to the matching label, execution runs on through every following case until a break or the end of the switch; stacking labels on one body is the legitimate use.

    A case label is only an entry point. switch jumps to the label that matches, or to default when none does, and then runs statements in order, straight past any later labels, until it meets break or the closing brace. A forgotten break is therefore a silent bug, which gcc flags with -Wimplicit-fallthrough, part of -Wextra. The deliberate use is stacking labels so that several values share one body, as the vowel test below does, and occasionally falling from one case into a shared tail. C23 adds the [[fallthrough]] attribute to mark an intended fall-through; before it, a comment saying so was the convention. The controlling expression must be an integer, and every label a distinct integer constant.

    char c = 'e';
    switch (c) {
    case 'a': case 'e':
    case 'i': case 'o':
    case 'u':
        puts("vowel");
        break;
    default:
        puts("consonant");
    }
    // vowel
  2. 2.What is the difference between while, do-while and for loops in C?

    In short: while and for test their condition before each pass, so they may run zero times; do-while tests after the body, so it always runs at least once.

    The three loops are equally powerful and differ in where the test sits. while (cond) checks before every pass. for (init; cond; step) is a while loop with its setup and step gathered into one line, and any of the three parts may be left empty, so for (;;) is the idiomatic infinite loop. do { ... } while (cond); checks after the body, so the body runs once even when the condition is false from the start, as the second loop below shows. That suits input loops that must read before they can test, and it is why multi-statement macros are wrapped in do { ... } while (0), which behaves as a single statement. The semicolon after a do-while's condition is required.

    int n = 7;
    while (n < 5)
        n++;
    printf("%d\n", n);
    // 7
    do {
        n++;
    } while (n < 5);
    printf("%d\n", n);
    // 8
  3. 3.How do you break out of two nested loops at once in C?

    In short: break leaves only the innermost loop or switch, so use a flag the outer loop checks, move the loops into a function and return, or use one goto.

    C has no labelled break, unlike Java. break exits the innermost enclosing loop or switch, and continue skips to the next iteration of the innermost loop only. To stop both loops as soon as a match is found there are three idioms. A flag, set in the inner loop and tested by the outer one, is the most common and the most verbose. Putting the loops in a function and returning the answer is usually the cleanest. And a goto to a label just after the outer loop, as below, is short and readable, one of the few uses of goto that style guides accept. A break inside a switch that sits inside a loop leaves the switch, not the loop, which catches people out.

    int g[2][3] = {
        {4, 7, 1}, {9, 3, 7}};
    int r, c;
    for (r = 0; r < 2; r++)
        for (c = 0; c < 3; c++)
            if (g[r][c] == 9)
                goto found;
    puts("none");
    found:
    printf("%d %d\n", r, c);
    // 1 0
  4. 4.Is goto ever acceptable in C?

    In short: Yes, for one pattern: jumping forward to a single cleanup section that releases resources in reverse order when a step fails, as the Linux kernel does.

    Most uses of goto make control flow hard to follow, which is why it is avoided, but C has no exceptions and no destructors, so a function that acquires several resources needs a way to release exactly the ones it got when a later step fails. The accepted idiom jumps forward to labels at the end of the function, ordered so that each label releases one resource and falls through to the next, as the code shows. It keeps a single exit path instead of repeating the cleanup in every error branch, and it is common in the Linux kernel and other large C code bases. Jumping backwards is where goto hurts readability, and jumping into the scope of a variable-length array is not allowed at all.

    int ok = 0;
    char *a = NULL, *b = NULL;
    a = malloc(16);
    if (!a)
        goto out;
    b = malloc(16);
    if (!b)
        goto free_a;
    ok = 1;
    free(b);
    free_a:
    free(a);
    out:
    printf("%d\n", ok);
    // 1
  5. 5.What counts as true and false in a C condition?

    In short: Any nonzero scalar value is true and zero is false, a null pointer included, and the comparison and logical operators produce the int values 1 and 0.

    C tests conditions numerically: an integer, floating-point value or pointer is false if it compares equal to zero and true otherwise, so if (p) means if (p != NULL), and if (-1) takes the branch. The relational, equality and logical operators all yield an int, 1 for true and 0 for false, which is why 5 > 3 prints as 1 and why expressions such as count += (x > 0) are legal. C99's <stdbool.h> added bool, true and false, and C23 makes them keywords, but underneath they are still 1 and 0. The flip side is that an accidental assignment in a condition compiles, because its value is simply tested, which is the bug this page's sample question shows.

    int t = 5 > 3, f = 5 < 3;
    printf("%d %d\n", t, f);
    // 1 0
    int *p = NULL;
    if (!p)
        puts("null is false");
    // null is false
    if (-1)
        puts("-1 is true");
    // -1 is true
  6. 6.How does the conditional operator ?: work in C?

    In short: cond ? a : b evaluates cond, then exactly one of a or b; it is an expression with a value, it groups right to left, and it needs parentheses inside larger expressions.

    The conditional operator is C's only operator with three operands. It evaluates the condition, then only the branch it selects, so p ? p->len : 0 is safe when p is null. Because it is an expression, it can appear where an if statement cannot, such as in an initialiser or a function argument. It groups right to left, so a ? b : c ? d : e means a ? b : (c ? d : e), which makes a readable chain for choosing one of several values, as below. The two branches are brought to a common type by the usual arithmetic conversions, so mixing an int and a double yields a double. Its precedence is just above assignment, so parenthesise it inside larger expressions.

    int m = 75;
    const char *g = m >= 90 ? "A"
        : m >= 60 ? "B" : "C";
    printf("%s\n", g);
    // B
  7. 7.Why can't a C switch work on strings?

    In short: A switch needs an integer controlling expression and integer constant case labels, and a string is an array compared through its characters, not a single integer.

    switch compares its controlling value with case labels that must be integer constant expressions known at compile time: integer and character values and enumeration constants. A string is an array of characters, and comparing two strings means comparing those characters with strcmp, not their addresses, so switch cannot express it, and the compiler rejects a pointer or a floating-point value as the controlling expression outright. The usual replacements are an if-else chain of strcmp calls, as below, a table that maps strings to values or function pointers, or parsing the string once into an enum and switching on that. Duplicate case values are also a compile error, and in C even a const int variable cannot be a case label.

    const char *cmd = "stop";
    switch (cmd) { }
    // does not compile
    if (strcmp(cmd, "stop") == 0)
        puts("stopping");
    // stopping

How the diagnostic asks it

One question from the C bank, exactly as a sitting would show it. The bank has 3 on control flow and 30 across C.

Control Flow · mediumCL-006

What does this C code print?

int x = 5;
if (x = 0)
    printf("zero ");
else
    printf("nonzero ");
printf("%d\n", x);
  1. 1zero 0
  2. 2nonzero 0correct
  3. 3nonzero 5
  4. 4zero 5

x = 0 is an assignment, not a comparison: it stores 0 in x, and the value of the whole expression is the value assigned, 0, which C treats as false. So the else branch prints nonzero, and x then prints as 0. nonzero 5 gets the branch right but forgets that the assignment changed x. zero 0 and zero 5 take the if branch, as if the condition were true; a condition whose value is 0 is false. gcc -Wall suggests parentheses around an assignment used as a truth value, and writing the constant first, 0 == x, turns this typo into a compile error.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 C questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated