December Code

C data types and operators interview questions, with answers

C's data types look simple until an interviewer asks what a line of arithmetic prints. The questions sit on the rules underneath: which sizes the standard actually fixes, what happens when an int overflows, how integer division rounds, how a signed value is converted when it meets an unsigned one, and which expressions have no defined answer at all. Most are asked as two or three lines of code and 'what is the output?', so every answer below comes with code, and every output shown was produced by compiling and running it with gcc.

The questions start with the types themselves, move through arithmetic and conversions, and end with the operators behind the classic traps. 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.What are the basic data types in C, and how big is each?

    In short: C fixes only minimums: char is one byte, short and int are at least 16 bits, long at least 32 and long long at least 64; the actual sizes come from the compiler and platform.

    Unlike Java, C does not fix the sizes of its integer types. It fixes their minimum ranges and their order, char <= short <= int <= long <= long long, and leaves the rest to the compiler and platform. char is 1 byte by definition. On today's mainstream 64-bit compilers int is 4 bytes and long long is 8, while long is 8 bytes on Linux and macOS but 4 on Windows, which is why portable code never assumes it. float and double are almost always IEEE 754 single and double precision. When the width matters, as in file formats and network protocols, use the fixed-width types from <stdint.h>, such as int32_t and uint8_t, and print a size with %zu, the format for size_t.

    printf("%zu %zu %zu\n",
        sizeof(char), sizeof(int),
        sizeof(long long));
    // 1 4 8
  2. 2.What happens when a signed int overflows in C?

    In short: The behaviour is undefined: the standard allows any result and optimisers assume it never happens; only unsigned arithmetic is defined to wrap around.

    C treats the two kinds of overflow completely differently. Unsigned integers are defined to wrap modulo 2^N, so 0u - 1 is UINT_MAX, 4294967295 with a 32-bit unsigned int, as the code shows. Signed overflow, such as INT_MAX + 1, is undefined behaviour: the program has no defined meaning from that point, and although the hardware usually wraps to a negative number, compilers optimise on the assumption that it cannot happen, so a test such as x + 1 > x may be compiled to always true. Interviewers use this to separate C from Java, where int overflow is defined to wrap. The safe approach is to check before the operation, as below, to use a wider type, or to build with -fsanitize=undefined while testing so the overflow is reported at run time.

    unsigned int u = 0;
    u = u - 1;
    printf("%u\n", u);
    // 4294967295
    int a = INT_MAX, b = 1;
    if (a > INT_MAX - b)
        puts("would overflow");
    // would overflow
  3. 3.How does integer division round in C, and what sign does % give?

    In short: Since C99, integer division truncates toward zero, so -11 / 3 is -3, and % takes the sign of the left operand, so -11 % 3 is -2.

    C99 settled what C89 left to the compiler: the quotient of two integers is the true quotient with its fraction discarded, which rounds toward zero for negative results, and a % b is defined so that (a / b) * b + a % b equals a. The remainder therefore has the sign of the dividend, as the code shows. Two consequences come up in interviews. First, n % 2 == 1 misses negative odd numbers, whose remainder is -1, so test n % 2 != 0 instead. Second, integer division happens before any conversion, so dividing two ints and storing the result in a double still loses the fraction; make one operand a double first. Dividing an integer by zero is undefined behaviour, and so is INT_MIN / -1, whose true result does not fit in an int.

    printf("%d %d\n", -11 / 3,
        -11 % 3);
    // -3 -2
    printf("%d\n", 11 % -3);
    // 2
  4. 4.Why does comparing a negative int with an unsigned value give the wrong answer in C?

    In short: The usual arithmetic conversions give both operands a common type, and when an int meets an unsigned type at least as wide, the int becomes unsigned, so a negative value turns huge.

    Arithmetic and comparison operators first bring their operands to a common type. Types narrower than int, such as char and short, are promoted to int, and then the lower-ranked type is converted to the other. The trap is mixed signedness: when an int meets an unsigned int, or a size_t, which is what sizeof and strlen return, the int is converted to unsigned, and -2 becomes a value just below the unsigned maximum. So with n a size_t holding 3, the test i < n is false for i = -2, and the code prints no. gcc and clang report it with -Wall -Wextra through -Wsign-compare. The fixes are to keep both sides signed, or to test for a negative value before comparing, which matters most for loop counters compared against strlen or sizeof.

    int i = -2;
    size_t n = 3;
    puts(i < n ? "yes" : "no");
    // no
  5. 5.Why do expressions like i = i++ have no defined result in C?

    In short: They modify the same variable twice, or modify it and read it, with nothing sequencing the two, which the C standard declares undefined behaviour.

    C does not evaluate the parts of an expression in a fixed order, and the side effect of i++ may take place at any point before the next sequence point. When one expression modifies a variable twice, or modifies it and also reads it for another purpose, with no sequence point in between, the behaviour is undefined, and i = i++ is exactly that: an assignment to i and an increment of i, unsequenced relative to each other. Compilers genuinely give different results for such code, and gcc warns that the operation may be undefined. The practical rule is one side effect per variable per expression; statements such as k++; or m = k + 1; are always fine. Answer keys that print a single number for these expressions are simply wrong.

    int n = 3;
    n = n++;
    // undefined behaviour
    int k = 3;
    k++;
    printf("%d\n", k);
    // 4
  6. 6.What is the difference between & and && in C, and why does if (x & 1 == 0) never run?

    In short: && is logical AND and skips its right operand when the left is false; & is bitwise AND; and == binds tighter than &, so x & 1 == 0 means x & (1 == 0).

    && and || work on truth values: they yield 1 or 0 and short-circuit, so in p != NULL && *p == 'a' the dereference runs only when p is not null. & and | work bit by bit on integers and always evaluate both operands. The classic bug comes from precedence: the equality operators bind tighter than &, ^ and |, a leftover from early C, so x & 1 == 0 is parsed as x & (1 == 0), which is x & 0, always 0. The even test below therefore fails even for 4. Parenthesise every bitwise test, (x & 1) == 0, and gcc -Wall points out the missing parentheses. The same rule catches flag checks such as if (mode & READ != 0).

    int x = 4;
    if (x & 1 == 0)
        puts("even");
    else
        puts("not even?");
    // not even?
    if ((x & 1) == 0)
        puts("even");
    // even
  7. 7.Is char signed or unsigned in C?

    In short: Plain char is its own type whose signedness the platform chooses: signed with x86 compilers, unsigned on most ARM ones, so use signed char or unsigned char when it matters.

    C has three character types, char, signed char and unsigned char, and plain char behaves like one of the other two, as the platform's ABI decides. On x86 and x86-64 it is signed, so storing 200 in a char gives -56 after the conversion, while an unsigned char holds 200, which is what the code prints. On ARM Linux and Android plain char is unsigned, so the same code prints 200 twice. This matters when a char is used as a small number or an array index, when bytes read from a file are compared with values above 127, and with functions such as isdigit and toupper, which need an argument representable as unsigned char, so a negative char must be cast first. For raw bytes, use unsigned char or uint8_t.

    char c = 200;
    unsigned char u = 200;
    printf("%d %d\n", c, u);
    // -56 200
  8. 8.What does the comma operator do in C?

    In short: It evaluates its left operand, discards that value, then evaluates the right operand, whose value becomes the value of the whole expression.

    The comma operator evaluates the left operand for its side effects, throws its value away, and yields the right operand, with a sequence point in between, so the left side is complete before the right side starts. It has the lowest precedence of all the operators, which is why int a = (5, 9); needs the parentheses: without them the comma would separate two declarators instead. Its honest use is stepping two variables in one loop, as in i++, j-- below, and in for headers such as for (i = 0, j = n - 1; i < j; i++, j--). The commas that separate declarators or function arguments are punctuation, not the operator, and carry none of its guarantees. gcc warns when the left operand has no effect.

    int a = (5, 9);
    int i = 0, j = 4;
    while (i < j)
        i++, j--;
    printf("%d %d %d\n", a, i, j);
    // 9 2 2

How the diagnostic asks it

One question from the C bank, exactly as a sitting would show it. The bank has 4 on data types & operators and 30 across C.

Data Types & Operators · easyCL-001

What does this C code print?

int a = 7, b = 2;
float f = a / b;
printf("%.1f\n", f);
  1. 13.5
  2. 24.0
  3. 33.0correct
  4. 43

a / b divides two ints, so C performs integer division and discards the fraction: 7 / 2 is 3. Only then is the int 3 converted to float for the assignment, and %.1f prints it with one decimal place, as 3.0. 3.5 needs a floating-point division, where one operand is already floating point before dividing, as in (float)a / b or a / 2.0. 4.0 assumes the result is rounded to the nearest whole number; integer division truncates toward zero. 3 is what %d would print for an int; %.1f always shows one decimal place.

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