Skip to content
Interview Pilot Logo

Interview Pilot

AI
Interview Pilot
Interview AssistantHow to UseReviewsPricing
LoginStart for Free
Resources

Interviews

Interview Questions About C: 10 Topics with Sample Answers

Practice interview questions about C, from pointers and memory to volatile and macros. Each of 10 topics has sample answers, pitfalls, and tips.

Interview Pilot Editorial Team

Updated November 1, 2026

20 min read

Interview Questions About C: 10 Topics with Sample Answers

You're sharing your screen in a technical interview. The interviewer asks what a pointer stores, and you answer confidently: an address. Then they show a linked-list cleanup loop that frees a node before reading its next member. You hesitate, suggest setting the pointer to NULL, and realize that your definition doesn't explain how to recover the rest of the list.

That follow-up is where interview questions about C become less about vocabulary and more about engineering judgment. A strong candidate tracks which objects exist, which pointers still refer to them, and which operations the language permits. Getting the expected output isn't enough if the program reaches it through undefined behavior.

C's close relationship with systems programming helps explain that emphasis. Unix was rewritten largely in C in 1973, demonstrating that an operating system could be implemented in a higher-level language rather than almost entirely in assembly, as documented in the history of C). Portability and direct control remain useful context, but an interview answer needs to connect those ideas to concrete code.

Prepare to explain allocations, array decay, storage duration, and const before moving into sequencing, structure layout, hardware access, callbacks, linked lists, and macros. For each topic, practice the follow-up that changes an assumption. That's how you move from recognizing a familiar question to defending an answer when the interviewer changes the code.

1. Dynamic Memory Management and Pointer Lifetime Errors

The interviewer's probe: A dynamic array needs more capacity. Why might items = realloc(items, bytes) leak memory, and what happens to pointers into the old allocation?

What it tests: Ownership, allocation failure, and object lifetime, not just whether you remember the allocation functions.

A strong answer explains that malloc returns uninitialized storage, while calloc initializes the allocated bytes to zero. For a nonzero requested size, realloc may move the allocation; if it fails, the original allocation remains valid.

The answer that survives the follow-up

Use a temporary pointer: int *tmp = realloc(items, bytes);. Check tmp before assigning it to items, and validate the capacity calculation before multiplying by sizeof(*items). After successful reallocation, rebuild any interior pointers from indexes rather than retaining pointers into the old object.

The offer-losing answer: Setting the original pointer to NULL makes every alias safe. It doesn't. Other pointers can still refer to storage whose lifetime has ended.

  • Linked-list cleanup: Save node->next before freeing node.
  • Returned buffers: Don't return a pointer to an automatic local array.
  • Failure paths: State who releases every successfully allocated object if a later operation fails.

Practical rule: Explain ownership before explaining cleanup. If nobody can name the owner, adding more calls to free won't fix the design.

Use AddressSanitizer or Valgrind and reproduce failures before fixing code. Connect allocation behavior to broader process-memory concepts through these operating system interview questions.

Keep track of the object behind each address, not just the pointer variable.

A contemplative young man holding a glowing digital cube with colorful data flow arrows pointing towards it.

2. Pointers vs. Arrays and Array Decay

An interviewer writes void print(int a[]) and asks you to calculate the caller's array length inside the function. The trap is the parameter declaration, not the arithmetic.

An array is an object containing elements. A pointer is an object that stores a pointer value. In many expressions, an array expression converts to a pointer to its initial element, but that conversion doesn't make the underlying types interchangeable.

Explain what changes at the function boundary

Strong sample answer: In this parameter declaration, a is adjusted to pointer type. Consequently, sizeof(a) measures the pointer, not the caller's array. I'd pass the element count separately using a size_t parameter.

At the caller, sizeof(array) / sizeof(array[0]) works for an actual array object. Applying the same expression to a pointer doesn't recover the allocation's length.

  • Writable string storage: char s[] = "hello" creates an array initialized from the literal.
  • String-literal pointer: const char *p = "hello" expresses that the characters mustn't be modified through p.
  • API boundary: A buffer pointer needs a length or another explicit termination contract.

The wrong answer under pressure: Arrays are constant pointers. That shortcut fails when the interviewer asks about assignment, sizeof, or &array, whose type is a pointer to the whole array.

For a worked exercise, implement a function that searches an integer array. Explain why its parameters need both the starting address and the element count, and why a pointer alone cannot establish the accessible bounds.

The useful distinction is between a container and a reference to its contents.

A woman pointing at a red cube in a row of rainbow-colored watercolor cubes on a white background.

3. Storage Classes auto, static, extern, and register

Can a variable disappear from scope without its object ceasing to exist? That follow-up exposes whether you've confused visibility with lifetime.

A function-local static int counter; has block scope, but its object has static storage duration. The name isn't accessible outside the block, while the stored value persists between calls. An ordinary automatic local object generally exists only for the corresponding block execution.

Strong sample answer: Scope describes where a name is visible, storage duration describes an object's lifetime, and linkage determines whether declarations identify the same entity. I'd explain those separately before interpreting any keyword.

Compare the declarations in real code

  • auto: In traditional explicit-type declarations, it specifies automatic storage duration for a block-scope object. Ask which language version the interview targets before discussing newer declaration forms.
  • Local static: Retains state across calls, which can be useful for counters but introduces shared mutable state.
  • File-scope static: Gives an object or function name internal linkage.
  • extern: Commonly declares an object defined elsewhere; an initializer can make an extern declaration a definition.
  • register: Doesn't force hardware-register allocation. In traditional C usage, it also restricts taking the declared object's address.

The answer that raises concern: Static means global. A static local object is a direct counterexample.

As a practical exercise, declare a shared status object with extern in a header and define it in a source file. Then make a helper function static and explain why callers in other translation units shouldn't depend on it.

Don't claim automatic variables always live on a stack. That's an implementation choice, not the definition of automatic storage duration.

4. const Pointer Qualifiers and Their Placement

Start with const int *p, then move const to produce int *const p. An interviewer who asks what assignments remain legal is testing declarator fluency and API design, not your ability to recite “read-only.”

For const int *p, you may change the pointer, but you can't modify the pointed-to integer through that pointer. For int *const p, the pointer itself can't be reassigned after initialization, but the pointed-to integer can be modified if the underlying object permits it.

Read outward from the identifier

Strong sample answer: In const int *const p, both restrictions apply. However, a pointer to const-qualified data doesn't necessarily point to an object originally defined as const; it restricts access through that pointer.

That distinction matters when an interviewer introduces another alias. If the actual object is mutable, another valid non-const pointer may modify it, and reads through p can observe the changed value.

  • Read-only input: Use const struct config *cfg when a function shouldn't modify configuration through its parameter.
  • Fixed pointer: Use a const-qualified pointer when reassignment would violate a local invariant.
  • Lookup data: A static const array expresses persistent, read-only table data without exposing a mutable global interface.

Casting away const changes the access type. It doesn't make an object defined as const writable.

The offer-losing answer: A cast makes the write legal. Modifying an object defined with const-qualified type through a cast produces undefined behavior.

For practice, design a string-inspection function and explain why it accepts const char *. Then explain why top-level const on a pointer parameter doesn't prevent the caller from changing its own pointer.

5. Undefined Behavior and Sequence Points

The probe: What does i = i++ print?

The strongest response rejects the premise: this expression has undefined behavior in C because the modifications aren't properly sequenced. There isn't a portable output to calculate, and running it doesn't establish a language guarantee.

The interviewer may then ask whether undefined behavior always causes a segmentation fault. It doesn't. A program may appear to work, fail only with optimization enabled, or corrupt unrelated state.

Give a diagnosis, not a prediction

Strong sample answer: I'd separate the operations into statements with a clear intended order, then inspect surrounding code for similar sequencing problems. I'd compile with -Wall -Wextra -Wpedantic and use UndefinedBehaviorSanitizer, while recognizing that no diagnostic tool proves the absence of undefined behavior.

Distinguish the categories clearly:

  • Undefined behavior: The language imposes no requirements on the execution.
  • Unspecified behavior: The language permits alternatives without requiring the implementation to document its choice.
  • Implementation-defined behavior: The implementation must document its selected behavior.

Signed integer overflow and out-of-bounds access belong in the same preparation session. For each, explain the precondition that makes the operation valid rather than merely naming the defect.

Toptal's C interview questions provide useful conceptual prompts, but your rehearsal should extend into reproductions, warnings, sanitizers, and repairs.

The wrong answer under pressure: My compiler prints the expected value, so it's safe. A compiler run is an observation about that execution, not evidence that the source has defined semantics.

6. Struct Padding, Alignment, and sizeof

An interviewer shows a structure containing char, int, and another char, then asks for its size. Don't guess a universal byte count. Ask about the target implementation or explain why the declaration alone doesn't establish a portable answer.

Compilers may insert padding between members to satisfy alignment requirements and at the end to support arrays of the structure. The resulting layout depends on the implementation's type sizes and alignment rules.

Strong sample answer: I'd inspect sizeof, offsetof, and the implementation's alignment facilities. Reordering members may reduce padding, but I'd first check whether the layout is externally visible or constrained by an interface.

The follow-up is usually about serialization

Can you write the structure directly to a network socket? Not as a portable wire format. Member layout, padding, byte order, and representation can differ between systems.

  • Protocol messages: Encode fields explicitly into byte buffers.
  • Memory inspection: Use offsetof to examine member offsets rather than inferring them.
  • Packed structures: Treat compiler-specific packing as a deliberate trade-off, not a universal fix.

The offer-losing answer: sizeof(struct) is always the sum of its members. A related mistake is using memcmp as a general test of member-wise equality, since padding can differ.

If you propose member reordering for performance, measure it. An ACM study comparing compiler and language effects in HPC benchmarks found that compiler selection sometimes affected performance more than language choice.

For a cache-sensitive array of records, compare representative workloads using the same compiler, flags, and hardware. Preserve correctness tests alongside the benchmark.

7. The volatile Keyword

The wrong answer to eliminate immediately: volatile makes a variable thread-safe.

It doesn't provide atomic read-modify-write operations, establish interthread synchronization, or replace a mutex. The interviewer's revealing follow-up is whether a volatile shared counter can safely be incremented concurrently.

Separate hardware access from synchronization

Strong sample answer: Volatile-qualified accesses constrain compiler optimization according to the implementation's rules. They're relevant when values can change outside ordinary visible program flow, such as hardware registers, but shared-memory concurrency requires atomics or synchronization.

For a memory-mapped status register, firmware may repeatedly perform volatile reads until hardware signals readiness. The register's address, access width, and ordering requirements must still come from the platform's hardware contract.

  • Thread communication: Use _Atomic, facilities from <stdatomic.h>, or locks as appropriate.
  • Signal notification: A volatile sig_atomic_t flag is a conventional constrained mechanism for communicating with a signal handler.
  • Read-only hardware state: const volatile can express a register that software reads but mustn't modify through that access path.

Interview distinction: const limits permitted modification through an access path. volatile concerns observable accesses. Neither keyword means thread synchronization.

The visual reminder is useful: protecting an object's handling isn't the same as coordinating every actor that touches it.

A woman carefully placing a white block into a clear container filled with colorful geometric shapes.

Practice these distinctions with embedded systems interview questions, particularly register polling and interrupt-related state. Don't stop at selecting the keyword; explain what guarantees must come from the compiler, processor, and device documentation.

8. Function Pointers and Callbacks with qsort

The interviewer asks you to sort integers with qsort, then challenges your comparator with extreme values. If you returned a - b, signed overflow may invalidate the implementation.

A function pointer allows code to invoke behavior supplied by its caller. For qsort, the comparison callback receives pointers to elements through const void * parameters and must return a negative value, zero, or a positive value according to their ordering.

Defend the callback contract

Strong sample answer: I'd convert each argument to const int *, dereference it, and compare the values without subtraction. The expression (a > b) - (a < b) provides the required sign without overflowing from subtracting the original integers.

The generic pointer interface lets the sorting function handle different element types. It doesn't remove type obligations: the comparator must interpret the objects according to their actual element type.

  • Element width: Pass sizeof(*items) so the call stays consistent with the array's type.
  • Ordering: Keep comparisons consistent and avoid modifying elements inside the comparator.
  • Equal keys: Don't assume qsort preserves their original relative order.

The answer that loses confidence: Any function becomes a valid callback if you cast its address. Casting a function pointer doesn't repair an incompatible function signature.

A useful follow-up asks you to sort an array of pointers to strings. Now the comparator arguments point to array elements that are themselves pointers, so another level of indirection is required.

Outside sorting, use the same reasoning for event handlers or a command-dispatch table. Explain the callback's signature, argument lifetime, and ownership contract before discussing declaration syntax.

9. Reversing a Singly Linked List

Reverse the links without allocating replacement nodes. That instruction tests whether you can preserve access to data while changing the structure that makes it reachable.

Use the diagram to trace the unreversed remainder before changing any link.

A step-by-step infographic illustrating the algorithm for reversing a singly linked list in computer programming.

Strong sample answer: Maintain prev, curr, and next. Save curr->next into next, redirect curr->next to prev, advance prev to curr, and advance curr to next. When curr becomes null, return prev as the new head.

Explain the invariant aloud

The prefix ending at prev is already reversed, while curr identifies the untouched remainder. Saving the next pointer before changing the link preserves access to that remainder.

  • Empty input: Return a null head without dereferencing it.
  • Minimal lists: Trace a single node and a short chain by hand.
  • Input contract: Clarify whether cycles are possible before assuming traversal terminates.

The offer-losing mistake: Overwriting curr->next before saving it, then trying to use that modified link to advance. You've redirected the path you still needed.

The iterative approach takes linear time and constant auxiliary space. A recursive version uses call-stack space proportional to the list length, which matters for long lists.

Watch the pointer updates, then reproduce them without the walkthrough.

Extend the exercise with coding interview questions. After reversal, verify reachability, termination, and cleanup rather than checking only the printed values.

10. Macros vs. Inline Functions and Preprocessor Pitfalls

A candidate fixes #define SQUARE(x) x * x by adding parentheses. The interviewer then supplies i++ as the argument. Parentheses repair precedence, not repeated evaluation.

Macros operate through preprocessing-token substitution. If a parameter appears repeatedly in the replacement, an argument with side effects may be evaluated repeatedly and can create undefined behavior.

Strong sample answer: For a typed arithmetic helper, I'd prefer a static inline function. Function arguments are evaluated before the body executes, and using the parameter repeatedly doesn't reevaluate the caller's expression.

Know what inline doesn't promise

The inline keyword doesn't guarantee machine-code substitution or faster execution. A compiler can inline functions without that keyword and decline to inline functions declared with it.

Use the tool that fits the requirement:

  • Typed computation: Prefer a function for type checking and predictable argument evaluation.
  • Conditional compilation: Use preprocessing directives for build-time feature selection.
  • Token operations: Macros remain useful for stringification and token pasting.
  • Constants: Consider an enumeration constant or a const-qualified object according to the required type and context.

The wrong answer under pressure: A fully parenthesized macro behaves exactly like a function. It still lacks function-call evaluation semantics and ordinary parameter type checking.

For a worked exercise, inspect the expansion of a square macro with gcc -E. Test an arithmetic expression as the argument, then a side-effecting expression, and explain why the failures have different causes.

Also review assert and NDEBUG. Never put essential program work inside an assertion expression, because disabling assertions mustn't remove behavior required for correctness.

How the 10 C interview topics compare by core concept, main pitfall, and what they test

Item Core Concept Main Pitfall What It Tests
Dynamic Memory Management and Pointer Lifetime Errors Heap allocation, resizing, ownership, and freeing memory Dangling pointers, use-after-free, double frees, leaks, or mishandled realloc failures Safe allocation, ownership, and pointer lifetime reasoning
Pointers vs. Arrays and Array Decay Arrays often convert to pointers, but remain distinct types Using sizeof on an array parameter as if it retained the original array size Memory layout, array decay, and function parameter behavior
Storage Classes: auto, static, extern, and register Storage duration, scope, and linkage of variables Confusing when an object exists with where its name is visible Understanding variable lifetime, visibility, and cross-file declarations
const Pointer Qualifiers and Their Placement const can qualify the pointed-to value, the pointer, or both Misreading which part of a pointer declaration is immutable C declarator fluency and const-correct API design
Undefined Behavior and Sequence Points Sequencing rules and behaviors the C standard does not define Assuming expressions with unsequenced conflicting accesses have predictable results Distinguishing undefined, unspecified, and implementation-defined behavior
Struct Padding, Alignment, and sizeof Alignment requirements can add padding within and after structs Assuming struct size equals the sum of member sizes or is portable Memory layout, alignment, and portability reasoning
The volatile Keyword Volatile-qualified accesses may change outside visible code flow Treating volatile as atomicity or thread synchronization Distinguishing hardware-facing access constraints from concurrency tools
Function Pointers and Callbacks with qsort Function pointers let generic code invoke caller-supplied behavior Incorrect comparator logic, unsafe casts, or subtraction overflow Callback declarations, generic programming, and comparator design
Reversing a Singly Linked List Reverse node links in place using pointer updates Losing the next node or mishandling empty and single-node lists Pointer manipulation, edge cases, and time and space complexity
Macros vs. Inline Functions and Preprocessor Pitfalls Textual macro expansion versus typed inline functions Repeated argument evaluation, missing parentheses, and weak type checking Preprocessor behavior and choosing safe reusable code constructs

Turning These Ten Topics Into a Practice Plan

Pick two topics per session and practice them as explanations, implementations, and follow-ups. Start by writing the answer by hand without reference material. Then turn the answer into a small program whose behavior you can test.

For memory management, build a resizable buffer and deliberately exercise allocation-failure cleanup. For arrays, compare an actual array expression with an adjusted function parameter. For callbacks, test values that would expose comparator overflow instead of choosing only convenient inputs.

Make each session produce evidence

  • State the contract: Describe valid inputs, ownership, lengths, and relevant language-version assumptions.
  • Write the implementation: Keep the example small enough that you can explain every access and mutation.
  • Test the boundary: Include empty inputs, capacity limits, invalid data where applicable, and cleanup after errors.
  • Explain the pitfall: Say aloud what a plausible wrong answer overlooks and how your implementation avoids it.

Compile suitable exercises with warnings enabled and use AddressSanitizer and UndefinedBehaviorSanitizer. A practical development command can include -Wall -Wextra -Wpedantic -g -fsanitize=address,undefined, subject to toolchain support. Read diagnostics before changing the code, then rerun the failing input after the repair.

Don't equate passing tests with memory safety. An ACM evaluation of automated repair for real-world C vulnerabilities reported an 88.2% Pass@1 rate, while 87.3% of generated patches were memory-safe. The distinction matters: functional results and memory-safety validation answer different questions.

Rehearse the interviewer's next question

After solving an exercise, change an assumption. Ask what happens if reallocation moves the buffer, a callback receives pointer elements, or a list contains a cycle. This forces you to reason from the contract instead of recognizing the original prompt.

Record your spoken explanation and listen for unsupported shortcuts such as arrays are pointers, volatile is thread-safe, or static means global. Replace each shortcut with a precise statement and a small counterexample. If English isn't your first language, rehearse concise technical sentences rather than memorizing a long speech.

Our software engineering question sets at Interview Pilot cover programming, data structures, algorithms, debugging, and testing, so you can extend these C drills into a broader preparation schedule. Keep the emphasis on explaining your reasoning independently, and use guided practice to expose gaps rather than conceal them.

Finish each session by revisiting the question you handled least confidently. Write the ownership rule, language guarantee, or invariant you missed, then solve a changed version without looking at the original answer. You're ready when you can defend both the implementation and the reason the tempting alternative is wrong.


Interview Pilot offers guided AI mock interviews with answer evaluations and a searchable question bank for technical preparation. Visit Interview Pilot to rehearse your C explanations, practice follow-up questions, and build a broader software engineering interview routine.

Topics

C interview questions

C programming interview

C pointers interview

C coding interview

C interview prep

Continue reading

What Is Technical Interview Questions Explained

Interviews

What Is Technical Interview Questions Explained

Learn what is technical interview questions, the main types, how interviewers score answers, and a practical 2026 prep plan.

November 1, 2026

20 min read

Sales Interview Pitch: How to Prepare and Deliver It

Interviews

Sales Interview Pitch: How to Prepare and Deliver It

Prepare a sales interview pitch or presentation that wins the room: a five-slide structure, discovery questions, objection handling and practice tips.

November 1, 2026

10 Tax Interview Questions You Should Prepare For

Interviews

10 Tax Interview Questions You Should Prepare For

Master the 10 most common tax interview questions with sample answers, key topics to review, and tips for entry-level to senior tax roles.

October 31, 2026

17 min read