Menu
CoddyTech

C Preprocessor Interview Questions

Macros, include guards and conditional compilation, with the macro traps that become output questions.

10 questions
  1. What is the C preprocessor?

    The preprocessor runs before the compiler and works on text: it pastes in #include files, expands macros, removes comments and keeps or drops code under #if. It knows nothing about types or scope, which is the root of every macro bug. gcc -E shows its output. See the preprocessor.

  2. What does SQUARE(2 + 3) give with #define SQUARE(x) x * x?

    C
    #include <stdio.h>
    
    #define SQUARE(x) x * x
    
    int main(void) {
        printf("%d\n", SQUARE(2 + 3));
        return 0;
    }

    It prints 11. A macro substitutes text, so SQUARE(2 + 3) becomes 2 + 3 * 2 + 3. Parenthesize every parameter and the whole body:

    #define SQUARE(x) ((x) * (x))
    

    That still evaluates x twice, so SQUARE(i++) is undefined behavior; a static inline function has neither problem. See macros.

  3. How many times is i incremented in MAX(i++, j)?

    C
    #include <stdio.h>
    
    #define MAX(a, b) ((a) > (b) ? (a) : (b))
    
    int main(void) {
        int i = 5, j = 3;
        int m = MAX(i++, j);
        printf("%d %d\n", m, i);
        return 0;
    }

    It prints 6 7. The expansion ((i++) > (j) ? (i++) : (j)) increments i in the condition and again in the chosen branch, which yields 6. It is defined, since ?: has a sequence point after the condition, but a function would evaluate i++ once.

  4. What is the difference between a macro and a function?

    A macro is text substituted by the preprocessor: no type checking, arguments evaluated as many times as they appear, nothing for a debugger to step into. A static inline function is type-checked, evaluates each argument once and costs nothing when inlined, so prefer it. Only macros can stringify (#x), paste tokens (##) and use the caller's __FILE__ and __LINE__.

  5. What is an include guard, and how is it different from #pragma once?

    An include guard stops a header from being processed twice in one translation unit, which would redefine its types.

    #ifndef LIST_H
    #define LIST_H
    
    typedef struct Node Node;
    Node *list_push(Node *head, int value);
    
    #endif /* LIST_H */
    

    #pragma once does the same in one line; it is not standard C, but GCC, Clang and MSVC support it.

  6. What do the # and ## operators do in a macro?

    #param turns a macro argument into a string literal (stringification); a ## b glues two tokens into one (token pasting).

    C
    #include <stdio.h>
    
    #define SHOW(expr) printf(#expr " = %d\n", (expr))
    #define FIELD(name) int field_##name
    
    struct Config {
        FIELD(width);
        FIELD(height);
    };
    
    int main(void) {
        struct Config c = {640, 480};
        SHOW(c.field_width * c.field_height);
        SHOW(17 / 5);
        return 0;
    }

    It prints c.field_width * c.field_height = 307200 and 17 / 5 = 3. assert uses # to print the failing expression.

  7. What is conditional compilation, and where is it used?

    #if, #ifdef and #ifndef keep or drop code before compiling: debug logging, platform-specific code, flags from the build (gcc -DDEBUG).

    C
    #include <stdio.h>
    
    #define DEBUG 1
    
    #if DEBUG
    #define LOG(msg) printf("[%s:%d] %s\n", __func__, __LINE__, msg)
    #else
    #define LOG(msg) ((void) 0)
    #endif
    
    int main(void) {
        LOG("starting");
    #ifdef _WIN32
        printf("Windows build\n");
    #else
        printf("not a Windows build\n");
    #endif
        return 0;
    }

    It prints [main:12] starting and not a Windows build. __func__ is a predefined identifier, not a macro, so #if cannot test it.

  8. Why are multi-statement macros wrapped in do { ... } while (0)?

    So the macro acts as one statement everywhere, including an if without braces, and still needs its semicolon.

    #define SWAP_BAD(a, b) int t = a; a = b; b = t;
    #define SWAP(a, b) do { int t_ = (a); (a) = (b); (b) = t_; } while (0)
    
    if (x > y)
        SWAP(x, y);      /* the whole swap is the if body */
    else
        puts("sorted");
    

    With SWAP_BAD only the first statement belongs to the if. Plain braces fail too: in {...}; the ; ends the if, so the else does not compile.

  9. What is the difference between #define and typedef?

    typedef creates a real alias for a type, which the compiler checks and scopes; #define is text the preprocessor pastes in before compilation. The difference shows with pointers:

    C
    #include <stdio.h>
    
    #define INT_PTR int *
    typedef int *IntPtr;
    
    int main(void) {
        int x = 1, y = 2;
        INT_PTR a, b;   /* expands to: int *a, b;  so b is a plain int */
        IntPtr c, d;    /* both are int * */
        a = &x;
        b = y;
        c = &x;
        d = &y;
        printf("%d %d %d %d\n", *a, b, *c, *d);
        return 0;
    }

    It prints 1 2 1 2, and b = y compiles because INT_PTR a, b; became int *a, b;, which makes b a plain int. A typedef name also follows block scope. Use typedef for types and keep #define for constants and macros. See typedef.

  10. How do you write a macro that takes a variable number of arguments?

    End the parameter list with ... and use __VA_ARGS__ in the body (C99).

    C
    #include <stdio.h>
    
    #define LOG(level, ...) printf("[" level "] " __VA_ARGS__)
    
    int main(void) {
        LOG("INFO", "server started on port %d\n", 8080);
        LOG("WARN", "disk at %d%%\n", 91);
        LOG("INFO", "shutting down\n");
        return 0;
    }

    It prints [INFO] server started on port 8080, [WARN] disk at 91% and [INFO] shutting down. The format string is part of ... on purpose: with #define LOG(fmt, ...) printf(fmt, __VA_ARGS__), a call with no extra arguments leaves a trailing comma. GCC's , ##__VA_ARGS__ and C23's __VA_OPT__(,) remove that comma. When you do not need the caller's __LINE__, a variadic function built on vprintf is the better tool.

Coddy programming languages illustration

Learn to code with Coddy

Get started