Top 60 C Interview Questions (2026)

The 60 C questions interviewers actually ask, with direct answers, runnable code, and what the interviewer is listening for. Grouped for freshers, intermediate, and experienced rounds.

60 questions with answers

What Is C?

Key Takeaways

  • C is a compiled, statically typed systems language that gives you direct control over memory through pointers.
  • It compiles to fast native code and underpins operating systems, embedded firmware, databases, and most language runtimes.
  • Interviews test whether you understand memory (the stack, the heap, pointers, undefined behavior), not just syntax.
  • This page is a question bank: work your tier first, read one tier up, and say answers out loud because delivery is evaluated too.

C is a general-purpose, compiled programming language designed by Dennis Ritchie at Bell Labs in the early 1970s. It's statically typed, close to the hardware, and small: a handful of keywords, manual memory management, and pointers that let you read and write raw addresses. That closeness is why C still runs operating system kernels, embedded devices, databases, and the runtimes of higher-level languages. In interviews, C questions probe whether you understand memory: how the stack and heap differ, what a pointer really holds, when behavior becomes undefined, and why a missing free() leaks. This page collects the 60 questions that come up most, each with a direct answer and runnable code. If you're building fundamentals, the C language reference on cppreference.com is a reliable path; increasingly the first C round is a live AI coding interview, so pair this bank with our AI interview preparation guides for the format side.

60Questions with answers on this page
3Difficulty tiers: freshers, intermediate, experienced
24Runnable C code snippets you can practice from
45-60 minTypical length of a C technical round

Watch: Python Full Course for Beginners

Video: Python Full Course for Beginners (freeCodeCamp.org, YouTube)

Test yourself and earn a certificate

10 quick questions. Score 70%+ to download your C certificate.

Jump to quiz

All Questions on This Page

60 questions
C Interview Questions for Freshers
  1. 1. What is C and why is it still widely used?
  2. 2. Is C compiled or interpreted, and what are the compilation stages?
  3. 3. What are the basic data types in C?
  4. 4. What is a pointer?
  5. 5. What is the difference between an array and a pointer?
  6. 6. Does C pass arguments by value or by reference?
  7. 7. What are format specifiers in printf and scanf?
  8. 8. How do operator precedence and associativity work in C?
  9. 9. What are the storage classes in C?
  10. 10. What does the const keyword do, and where does its position matter?
  11. 11. What loops does C provide and when do you pick each?
  12. 12. How are arrays laid out and indexed in C?
  13. 13. How are strings represented in C?
  14. 14. What is a function prototype and why declare one?
  15. 15. What does the sizeof operator do, and why isn't it a function?
  16. 16. What is the difference between #include with angle brackets and quotes?
  17. 17. What does typedef do?
  18. 18. What is the difference between pre-increment and post-increment?
  19. 19. What is the difference between && and &, or || and |?
  20. 20. What is a null pointer, and how is it different from an uninitialized pointer?
C Intermediate Interview Questions
  1. 21. What is the difference between the stack and the heap?
  2. 22. What is the difference between malloc, calloc, realloc, and free?
  3. 23. What is a memory leak and how do you prevent it?
  4. 24. What is a dangling pointer?
  5. 25. How does pointer arithmetic work?
  6. 26. What is a pointer to a pointer, and when do you need one?
  7. 27. What is a struct and how do you access its members?
  8. 28. What is the difference between a struct and a union?
  9. 29. What is structure padding and why does it happen?
  10. 30. What is a function pointer and where is it used?
  11. 31. How do preprocessor macros work, and what are their pitfalls?
  12. 32. What are the two meanings of the static keyword?
  13. 33. What does the volatile keyword do?
  14. 34. What is an enum and how is it stored?
  15. 35. How do you set, clear, and test a single bit?
  16. 36. Why are strcpy and strcat considered unsafe, and what do you use instead?
  17. 37. How does a C program read command-line arguments?
  18. 38. How does recursion use the stack, and what are its risks?
  19. 39. How do you read from and write to files in C?
  20. 40. How are multidimensional arrays laid out, and how do you pass them?
C Interview Questions for Experienced Developers
  1. 41. What is undefined behavior, and why does it matter so much in C?
  2. 42. Why is signed integer overflow undefined while unsigned overflow is defined?
  3. 43. How is a running C program laid out in memory?
  4. 44. What does the restrict keyword do?
  5. 45. What is the strict aliasing rule?
  6. 46. How do inline functions differ from macros, and what does inline actually promise?
  7. 47. What is const correctness and why does it matter in an API?
  8. 48. What is memory alignment and when does it matter?
  9. 49. For multithreaded code, why is volatile not enough, and what replaces it?
  10. 50. How do buffer overflows lead to security vulnerabilities, and how do you defend against them?
  11. 51. What is the difference between a declaration and a definition, and how does the linker use them?
  12. 52. What is endianness and when do you have to care about it?
  13. 53. What is a flexible array member and why use one?
  14. 54. What is the difference between static and dynamic linking?
  15. 55. What are setjmp and longjmp, and what are their dangers?
  16. 56. How do compiler optimization levels affect behavior, and why can -O2 expose bugs?
  17. 57. How do you write a generic container in C without templates?
  18. 58. A C program segfaults intermittently in production. Walk through your debugging approach.
  19. 59. What are the key differences between C and C++ that trip people up?
  20. 60. What are memory barriers and why do you need them in lock-free code?

C Interview Questions for Freshers

Freshers20 questions

The fundamentals every entry-level round checks. If any answer here surprises you, that's your study list.

Q1. What is C and why is it still widely used?

C is a compiled, statically typed, general-purpose language designed by Dennis Ritchie in the early 1970s. It's small and close to the hardware: manual memory management, pointers, and direct access to raw memory addresses.

It stays relevant because it produces fast native code with a tiny runtime, which is exactly what operating systems, device drivers, embedded firmware, and language runtimes need. When performance and control matter more than convenience, C is often the floor everything else sits on.

Key point: A one-line definition plus two concrete uses (kernels, embedded) beats a memorized feature list. Interviewers open with this to hear how you organize an answer.

Watch a deeper explanation

Video: C Programming Tutorial for Beginners (freeCodeCamp.org, YouTube)

Q2. Is C compiled or interpreted, and what are the compilation stages?

C is compiled, not interpreted. Source code is translated ahead of time into native machine code that runs directly on the CPU, so there's no interpreter sitting between your program and the hardware at run time. That's a big reason C is fast and produces standalone binaries.

The build runs in stages: preprocessing (expands #include and #define), compilation (C to assembly), assembly (assembly to object code), and linking (object files plus libraries into one executable). Knowing the stages helps you read errors: a preprocessor error, a compile error, and a linker error each point at a different phase.

  • Preprocess: handle #include, #define, and conditional compilation.
  • Compile: turn each translation unit into an object file.
  • Link: resolve symbols across object files and libraries into an executable.

From source to executable

1Preprocess
expand #include and #define, strip comments
2Compile
translate each .c file to assembly
3Assemble
turn assembly into object files (.o)
4Link
resolve symbols across objects and libraries into one binary

Each error type maps to a stage: 'undefined reference' is a linker error, not a compile error.

Key point: If you can The stage a given error comes from (undefined reference is a linker error), you sound like someone who has actually debugged builds.

Q3. What are the basic data types in C?

The core types are char, int, float, and double, each with sizes that depend on the platform rather than being fixed by the standard. Modifiers change them: short and long adjust size, signed and unsigned adjust range, and const and volatile adjust behavior.

The standard only guarantees minimum sizes and relationships (a long is at least as wide as an int), which is why portable code uses fixed-width types from stdint.h like int32_t when the exact width matters.

TypeTypical sizeHolds
char1 byteA single character or small integer
int4 bytesWhole numbers
float4 bytesSingle-precision decimals
double8 bytesDouble-precision decimals

Key point: Say 'sizes are platform-dependent, use stdint.h for exact widths'. That one sentence separates you from someone who assumes int is always 4 bytes.

Q4. What is a pointer?

A pointer is a variable that stores a memory address, the location of another variable, rather than a value itself. You declare one with a star (int *p), take an address with &, and read through it with * (dereferencing).

Pointers are why C is fast and dangerous at once: they let you pass large data cheaply, build linked structures, and touch memory directly, but a wrong address is undefined behavior. Almost every hard C interview question eventually comes back to pointers.

c
int x = 42;
int *p = &x;      /* p holds the address of x */
printf("%d\n", *p); /* 42, dereference to read x */
*p = 7;           /* writes through p, so x becomes 7 */
printf("%d\n", x);  /* 7 */

Key point: Draw a box for x and an arrow from p to it. Interviewers love candidates who reach for the whiteboard on pointer questions.

Watch a deeper explanation

Video: Pointers in C / C++ [Full Course] (freeCodeCamp.org, YouTube)

Q5. What is the difference between an array and a pointer?

An array is a fixed block of contiguous elements with its size known to the compiler; a pointer is a variable holding one address. They feel similar because an array name decays to a pointer to its first element in most expressions.

The differences show where the array type is kept: sizeof(arr) gives the whole array's bytes while sizeof(ptr) gives the pointer's size, and you can reassign a pointer but not an array name. Passing an array to a function passes a pointer, which is why the callee can't know the length.

c
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;          /* array decays to &arr[0] */

sizeof(arr);           /* 20 (5 * 4 bytes) */
sizeof(p);             /* 8 (size of a pointer) */

p++;                   /* legal: now points at arr[1] */
/* arr++;  <- illegal: array name is not modifiable */
ArrayPointer
StoresThe elements themselvesOne address
sizeofWhole array's bytesPointer's size
ReassignableNoYes
In a function paramDecays to a pointerStays a pointer

Key point: The sizeof difference is the classic follow-up. Have the '20 vs 8' example ready to say out loud.

Q6. Does C pass arguments by value or by reference?

C is always pass-by-value: a function receives a copy of each argument, so changing a parameter never changes the caller's variable. There's no true pass-by-reference in C.

You simulate reference semantics by passing a pointer: the pointer is still copied, but it copies an address, so the function can dereference it and modify the original. That's why swap functions take pointers.

c
void broken(int a, int b) {   /* copies, no effect outside */
    int t = a; a = b; b = t;
}

void swap(int *a, int *b) {   /* pointers reach the originals */
    int t = *a; *a = *b; *b = t;
}

int x = 1, y = 2;
swap(&x, &y);   /* x == 2, y == 1 */

Key point: Say 'always by value; pointers just pass an address by value'. That precise phrasing is what earns the point.

Q7. What are format specifiers in printf and scanf?

Format specifiers tell printf and scanf how to interpret each argument: %d for int, %f for float and double in printf, %c for char, %s for a string, %p for a pointer, and %u for unsigned.

They matter because C doesn't check them against the argument types at run time. A mismatch, like %d for a double, is undefined behavior, not a friendly error. In scanf you also pass addresses (&x), a spot beginners forget.

c
int age = 30;
double pay = 4.5;
printf("%d earns %.2f\n", age, pay);   /* 30 earns 4.50 */

int n;
scanf("%d", &n);   /* note the & : scanf needs an address */

Q8. How do operator precedence and associativity work in C?

Precedence decides which operator binds first (multiplication before addition), and associativity breaks ties between operators of equal precedence (most are left to right, assignment is right to left).

You don't memorize the whole table. You reach for parentheses when intent isn't obvious, because a wrong assumption here produces bugs the compiler won't flag, like *p++ which increments the pointer, not the value.

c
int r = 2 + 3 * 4;      /* 14, not 20: * binds first */
int a = 1, b = 2, c;
c = a = b;              /* right to left: a = 2, then c = 2 */

int arr[3] = {10, 20, 30};
int *p = arr;
int v = *p++;           /* v = 10, then p moves to arr[1] */

Key point: The *p++ example is a favorite trap. Explaining that ++ binds tighter than * here shows you read precedence correctly.

Q9. What are the storage classes in C?

The four storage classes are auto, register, static, and extern. They set a variable's lifetime (how long it lives) and linkage (where its name is visible).

auto is the default for locals (lifetime is the block). static gives a variable program lifetime and, at file scope, internal linkage. extern declares a name defined elsewhere. register is a hint to keep a variable in a CPU register, largely ignored by modern compilers.

ClassLifetimeNotes
autoBlockDefault for locals
staticWhole programKeeps value between calls; file scope limits visibility
externWhole programRefers to a definition in another file
registerBlockHint to use a register; mostly ignored today

Q10. What does the const keyword do, and where does its position matter?

const marks something as read-only after its initialization, so the compiler rejects any attempt to modify it. It documents your intent to callers and catches accidental writes at compile time rather than letting them corrupt data at run time. It's a safety and communication tool, not a performance one.

With pointers, position changes meaning: const int *p is a pointer to a constant int (you can't change the value through p), while int *const p is a constant pointer (you can't repoint p). Reading declarations right to left makes this clear.

c
const int *p1;     /* value is const: *p1 = 5 is illegal, p1 = &y is fine */
int *const p2 = &x;/* pointer is const: p2 = &y is illegal, *p2 = 5 is fine */
const int *const p3 = &x; /* both const */

Key point: Read the declaration from the variable name outward. That trick answers every 'what does this const mean' question they can throw.

Q11. What loops does C provide and when do you pick each?

C has for, while, and do-while. Use for when the iteration count or range is known up front, while when you loop until a condition changes, and do-while when the body must run at least once before the test.

do-while checks its condition at the bottom, so it always executes once. That's the distinction interviewers probe: menu prompts and input validation are the natural do-while cases.

c
for (int i = 0; i < 3; i++) { /* known count */ }

int n = 5;
while (n > 0) { n--; }        /* until condition changes */

int choice;
do {
    /* show menu, read choice */
} while (choice != 0);        /* runs at least once */

Q12. How are arrays laid out and indexed in C?

An array is a contiguous block of same-type elements. Indexing arr[i] is defined as *(arr + i): the compiler adds i element-widths to the base address, so index 0 is the first element.

C does no bounds checking. Reading or writing past the end is undefined behavior, one of the most common sources of crashes and security bugs, so you track lengths yourself.

c
int a[4] = {10, 20, 30, 40};
a[2];         /* 30 */
*(a + 2);     /* also 30: identical to a[2] */
2[a];         /* also 30, because a[i] == *(a+i) == *(i+a) */

Key point: The 2[a] trick surprises people and shows you understand a[i] is just pointer arithmetic. Use it sparingly but it lands.

Q13. How are strings represented in C?

A C string is a char array ending in a null terminator, the byte '\0'. There's no separate string type and no stored length; functions like strlen walk the array until they hit the null byte.

This is why buffers must have room for the terminator (a 5-character string needs 6 bytes), why forgetting the null byte causes reads to run off the end, and why repeatedly calling strlen in a loop is a hidden O(n) cost each time. String bugs in C are almost always terminator or length bugs.

c
char s[6] = "hello";  /* 5 chars + '\0' = 6 bytes */
printf("%zu\n", strlen(s)); /* 5, stops at '\0' */
s[5] == '\0';         /* true: the hidden terminator */

Reads to get a string's length: strlen scan vs a stored length

Character reads to find the length of a 1000-character string. strlen walks to the null byte; a length stored alongside the buffer is a single read.

strlen scan
1,000 reads
stored length
1 reads
  • strlen scan: O(n): reads every byte until '\0'
  • stored length: O(1): one read of a saved count

Q14. What is a function prototype and why declare one?

A prototype declares a function's name, return type, and parameter types before its use, without the body. It lets the compiler check calls for the right argument types and count.

Without a prototype, an older compiler would assume the wrong types and produce silent bugs; modern C requires the declaration to be in scope. Prototypes usually live in header files so many source files can call the function.

c
int add(int a, int b);   /* prototype, often in a header */

int main(void) {
    int s = add(2, 3);   /* checked against the prototype */
    return 0;
}

int add(int a, int b) { return a + b; }

Q15. What does the sizeof operator do, and why isn't it a function?

sizeof yields the size in bytes of a type or object, evaluated at compile time. sizeof(char) is 1 by definition, and every other size is measured in units of char.

It's an operator, not a function, so the compiler computes it without running code and its operand isn't evaluated: sizeof(x++) doesn't increment x. Use it to size allocations (malloc(n * sizeof(int))) and to count array elements portably.

c
int arr[10];
size_t count = sizeof(arr) / sizeof(arr[0]);  /* 10, portable */

int *block = malloc(count * sizeof(*block));   /* size from the pointer target */

Key point: Using sizeof(*ptr) instead of sizeof(int) in malloc is a small tell that you write maintainable C: the size follows the type automatically.

Q16. What is the difference between #include with angle brackets and quotes?

#include <stdio.h> searches the standard system include paths first; #include "myheader.h" searches the current directory (or the file's directory) first, then falls back to the system paths.

So angle brackets are for library headers and quotes are for your own project headers. Both just paste the file's text in during preprocessing.

c
#include <stdio.h>     /* system header */
#include "utils.h"     /* your project header */

Q17. What does typedef do?

typedef creates an alias for an existing type. It doesn't make a new type; it gives an existing one a shorter or clearer name, which helps most with structs and function pointers.

typedef struct { ... } Point; lets you write Point instead of struct Point everywhere. Overusing it to hide pointers (typedef int *IntPtr) is discouraged because it obscures that a value is a pointer.

c
typedef struct {
    int x;
    int y;
} Point;

Point origin = {0, 0};   /* no 'struct' keyword needed */

Q18. What is the difference between pre-increment and post-increment?

Pre-increment (++i) increments i first, then the expression takes the new value. Post-increment (i++) yields the old value first, then increments. Both change i by one when used alone.

The difference only matters when the result is used in a larger expression. Chaining side effects on one variable in a single statement (i = i++ + 1) is undefined behavior, so keep increments simple.

c
int i = 5;
int a = ++i;   /* i = 6, a = 6 */
int j = 5;
int b = j++;   /* b = 5, j = 6 */

Q19. What is the difference between && and &, or || and |?

&& and || are logical operators that yield 0 or 1 and short-circuit: the right side is skipped when the left already decides the result. & and | are bitwise: they combine every bit of both operands and never short-circuit.

The bug this prevents is real: if (p != NULL && p->x) is safe because && stops before dereferencing a null pointer, while & would evaluate both sides and crash.

c
5 && 0;   /* 0  (logical AND) */
5 & 0;    /* 0  (bitwise: 101 & 000) */
5 | 2;    /* 7  (bitwise: 101 | 010) */

/* short-circuit guards the dereference */
if (p != NULL && p->ready) { /* safe */ }

Key point: The null-check example is the one to give. It proves you understand short-circuiting as a safety tool, not just a speed trick.

Q20. What is a null pointer, and how is it different from an uninitialized pointer?

A null pointer is a pointer set to NULL, a value guaranteed not to point at any object. It's a defined, checkable state: you test if (p == NULL). An uninitialized pointer holds whatever garbage was in its memory, so it might point anywhere.

The practical rule: initialize pointers, ideally to NULL, and check before dereferencing. Dereferencing NULL is a clean crash; dereferencing garbage is unpredictable corruption, which is far worse to debug.

c
int *good = NULL;   /* defined, testable */
int *bad;           /* indeterminate: may point anywhere */

if (good != NULL) { /* safe to skip */ }
/* *bad = 5;  <- undefined behavior */
Back to question list

C Intermediate Interview Questions

Intermediate20 questions

For candidates with working experience: memory, pointers in depth, structs, and the questions that separate users from understanders.

Q21. What is the difference between the stack and the heap?

The stack holds automatic variables and function call frames; it's fast, managed for you, and freed automatically when a function returns, but it's small and its size is fixed. The heap is a larger pool you allocate from by hand with malloc and release with free.

The trade-off drives design: stack memory is effortless but short-lived and limited, so returning a pointer to a local is a bug. Heap memory outlives the function and can be large, but every allocation is your responsibility to free.

StackHeap
Managed byCompiler (automatic)You (malloc/free)
LifetimeUntil the function returnsUntil you free it
SpeedVery fastSlower (bookkeeping)
SizeSmall, fixedLarge, limited by RAM

Key point: The trap answer is returning &local. If you volunteer why that dangles, you show you understand stack lifetime, not just the definition.

Watch a deeper explanation

Video: Pointers and dynamic memory - stack vs heap (mycodeschool, YouTube)

Q22. What is the difference between malloc, calloc, realloc, and free?

malloc(n) allocates n bytes, uninitialized. calloc(count, size) allocates and zero-initializes, and it also guards against multiplication overflow. realloc(ptr, n) resizes a block, possibly moving it and copying the old contents. free(ptr) returns a block to the allocator.

Two things bite people: always check the return for NULL, and with realloc, assign to a temporary first, because if it fails you'd otherwise lose the original pointer and leak it.

c
int *a = malloc(4 * sizeof(int));   /* uninitialized */
int *b = calloc(4, sizeof(int));    /* zeroed, overflow-safe */

int *tmp = realloc(a, 8 * sizeof(int));
if (tmp != NULL) a = tmp;           /* only overwrite on success */

free(a);
free(b);

Key point: The realloc-into-a-temporary detail is a senior tell at the intermediate level. Mention it before they ask.

Q23. What is a memory leak and how do you prevent it?

A memory leak is heap memory that's allocated but never freed and no longer reachable, so it stays reserved until the program exits. In a long-running process, leaks grow until it runs out of memory.

Prevent them by pairing every malloc with exactly one free, freeing on every exit path (including error returns), setting freed pointers to NULL, and running tools like Valgrind or AddressSanitizer that report leaks and use-after-free.

c
char *buf = malloc(256);
if (do_work(buf) != 0) {
    free(buf);     /* free on the error path too */
    return -1;
}
free(buf);
buf = NULL;        /* avoid accidental reuse */

Key point: Naming Valgrind or AddressSanitizer signals real experience. Anyone can say 'free your memory'; tool names show you've chased a leak.

Q24. What is a dangling pointer?

A dangling pointer points to memory that's already been freed or has gone out of scope. The pointer still holds the old address, but the memory it names is no longer valid, so using it is undefined behavior.

Common causes: using a pointer after free, returning the address of a local variable, or keeping a pointer into a block that realloc moved. The habit that prevents it is setting pointers to NULL right after freeing.

c
int *make(void) {
    int local = 5;
    return &local;   /* BUG: local dies when make() returns */
}

int *p = malloc(4);
free(p);
/* *p = 1;  <- dangling: p points at freed memory */
p = NULL;            /* the fix */

Q25. How does pointer arithmetic work?

Adding an integer to a pointer moves it by that many elements, not bytes: p + 1 advances by sizeof(*p). Subtracting two pointers into the same array yields the number of elements between them.

That's why array indexing and pointer walking are equivalent. Arithmetic is only defined within one array (plus one past the end); going further, or mixing pointers from different objects, is undefined behavior.

c
int a[5] = {10, 20, 30, 40, 50};
int *p = a;
p += 2;              /* moves 2 ints, points at a[2] == 30 */
printf("%d\n", *p); /* 30 */

int *end = &a[5];
ptrdiff_t n = end - a;   /* 5 elements */

Q26. What is a pointer to a pointer, and when do you need one?

A double pointer (int **pp) holds the address of a pointer. You need one when a function must modify a caller's pointer, not just the value it points to, for example allocating memory and handing the pointer back through a parameter.

The same idea underlies arrays of strings (char **argv) and dynamically built 2D arrays. The rule is consistent: to let a function change something in the caller, pass a pointer to that something, and a pointer's address is a double pointer.

c
void alloc_int(int **out) {
    *out = malloc(sizeof(int));  /* writes through to the caller's pointer */
    **out = 42;
}

int *p = NULL;
alloc_int(&p);   /* p now points at heap memory holding 42 */
free(p);

Key point: The 'function that allocates and returns via a parameter' example is exactly what interviewers hope to hear for 'when do you need a double pointer?'.

Q27. What is a struct and how do you access its members?

A struct groups related variables of possibly different types into one unit. You access members with the dot operator on a struct value (s.field) and the arrow operator through a pointer (p->field, shorthand for (*p).field).

Structs are how C builds records: a point, a linked-list node, a network packet. Passing a large struct by value copies the whole thing, so functions usually take a pointer to a struct instead.

c
typedef struct {
    char name[32];
    int age;
} Person;

Person a = {"Asha", 30};
a.age = 31;           /* dot on a value */

Person *p = &a;
p->age = 32;          /* arrow through a pointer */

Q28. What is the difference between a struct and a union?

A struct gives every member its own storage, so its size is at least the sum of its members. A union overlays all members in the same storage, so its size is that of its largest member and only one member holds a valid value at a time.

Unions save memory and model tagged variants (a value that's an int or a float depending on a separate tag field). Reading a union member you didn't last write is a portability hazard, so keep a tag alongside it.

structunion
StorageSeparate per memberShared, overlapped
SizeSum of members (plus padding)Largest member
Active membersAll at onceOne at a time
Typical useRecordsTagged variants, memory saving

Q29. What is structure padding and why does it happen?

The compiler inserts unused bytes between struct members so each one lands on an address its type prefers (alignment). This keeps memory access fast and, on some hardware, valid at all.

The effect is that sizeof(struct) can exceed the sum of member sizes, and member order changes the total. Ordering fields from largest to smallest usually minimizes padding, which matters for large arrays of structs or wire formats.

c
struct Bad  { char a; int b; char c; };   /* often 12 bytes: padding */
struct Good { int b; char a; char c; };   /* often 8 bytes: less padding */

printf("%zu %zu\n", sizeof(struct Bad), sizeof(struct Good));

Key point: Reordering fields to cut padding is the answer that shows you've thought about memory layout, not just correctness.

Q30. What is a function pointer and where is it used?

A function pointer stores the address of a function, so you can call it indirectly and pass behavior around as data. The declaration mirrors the function's signature: int (*op)(int, int) points at any function taking two ints and returning an int.

They power callbacks (qsort's comparator), dispatch tables that replace long switch statements, and plugin-style designs. typedef makes their gnarly syntax readable.

c
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }

int (*op)(int, int) = add;
op(2, 3);        /* 5 */
op = sub;
op(2, 3);        /* -1 */

Key point: qsort's comparator matters. That grounds an abstract topic.

Q31. How do preprocessor macros work, and what are their pitfalls?

Macros are text substitution done before compilation. Object-like macros (#define MAX 100) replace a token; function-like macros (#define SQ(x) ((x)*(x))) take arguments and paste them in. The compiler never sees the macro name.

The pitfalls are all about substitution without type checking: forgetting to parenthesize arguments and the whole body causes precedence bugs, and passing an expression with side effects (SQ(i++)) evaluates it twice. For anything nontrivial, an inline function is safer.

c
#define SQ(x) ((x) * (x))   /* parenthesize everything */
SQ(2 + 3);   /* ((2+3)*(2+3)) = 25, correct */

/* without inner parens, #define SQ(x) x*x gives 2+3*2+3 = 11 */

/* still risky: SQ(i++) increments i twice */

Q32. What are the two meanings of the static keyword?

Inside a function, static makes a local variable persist across calls: it's initialized once and keeps its value, though its scope stays local. At file scope, static gives a variable or function internal linkage, so it's private to that translation unit.

So static means 'persistent' for locals and 'file-private' for globals and functions. Both uses reduce surprises: state that survives calls, and helpers that don't leak into the global namespace.

c
int next_id(void) {
    static int counter = 0;  /* survives between calls */
    return ++counter;
}

static int helper(void) { return 1; } /* not visible to other files */

next_id();  /* 1 */
next_id();  /* 2 */

Q33. What does the volatile keyword do?

volatile tells the compiler that a variable can change outside the program's normal flow, so it must not cache the value in a register or optimize away reads and writes. Every access goes to actual memory.

It matters for memory-mapped hardware registers, variables shared with an interrupt handler, and flags set by a signal. It's about visibility of changes, not thread safety: volatile does not provide atomicity or ordering, which is a common misconception.

c
volatile int *reg = (int *)0x40021000; /* hardware register */
while (*reg == 0) { }                  /* re-read every time, not cached */

volatile sig_atomic_t stop = 0;        /* set by a signal handler */

Key point: The trap is calling volatile a threading tool. Saying 'it's about visibility, not atomicity' is the distinction they're testing for.

Q34. What is an enum and how is it stored?

An enum defines a set of named integer constants, improving readability over bare numbers. By default the first is 0 and each next one increments, but you can assign explicit values.

Under the hood an enum is an integer type, so enum values are ints and can be used in switch statements, array indices, and arithmetic. That integer nature is both convenient and a weakness: nothing stops an out-of-range value from being assigned.

c
enum Color { RED, GREEN = 5, BLUE };  /* RED=0, GREEN=5, BLUE=6 */

enum Color c = GREEN;
switch (c) {
    case RED:   break;
    case GREEN: break;
    case BLUE:  break;
}

Q35. How do you set, clear, and test a single bit?

Use a mask made by shifting 1 to the bit's position. Set a bit with OR (x |= 1 << n), clear it with AND of the inverted mask (x &= ~(1 << n)), and test it with AND (x & (1 << n)).

Bit manipulation is everyday work in embedded code, flags, and permissions. Toggling uses XOR (x ^= 1 << n). Keeping these four idioms sharp is expected in any C role that touches hardware or protocols.

c
unsigned x = 0;
x |= (1 << 3);        /* set bit 3   -> 0b1000 */
x &= ~(1 << 3);       /* clear bit 3 -> 0b0000 */
x ^= (1 << 1);        /* toggle bit 1 */
if (x & (1 << 1)) { } /* test bit 1 */

Key point: Write the four one-liners from memory. Interviewers for embedded and systems roles treat this as a baseline, not a bonus.

Q36. Why are strcpy and strcat considered unsafe, and what do you use instead?

strcpy and strcat write until they hit a null terminator with no idea of the destination's size, so a source longer than the buffer overflows it. Buffer overflows corrupt memory and are a classic security vulnerability.

Use the length-bounded versions: strncpy, strncat, or better snprintf, which take a size limit. Even strncpy has a sharp edge (it may not null-terminate on truncation), so many teams standardize on snprintf for building strings safely.

c
char dst[8];
/* strcpy(dst, "this is too long");  <- overflow */

snprintf(dst, sizeof(dst), "%s", "hello world"); /* safe, truncates */
dst[sizeof(dst) - 1] == '\0';                    /* always terminated */

Key point: Naming snprintf as the safe default, and knowing strncpy's null-termination gotcha, marks you as security-aware.

Q37. How does a C program read command-line arguments?

main can be declared int main(int argc, char *argv[]). argc is the argument count including the program name, and argv is an array of string pointers; argv[0] is the program name and argv[argc] is NULL.

Arguments arrive as strings, so numbers need parsing with atoi or the safer strtol. This is the standard entry point for any tool that takes options or file paths.

c
int main(int argc, char *argv[]) {
    for (int i = 1; i < argc; i++)
        printf("arg %d: %s\n", i, argv[i]);
    return 0;
}

Q38. How does recursion use the stack, and what are its risks?

Each recursive call pushes a new stack frame holding that call's parameters and locals. The frames unwind as calls return. Recursion is natural for problems that break into smaller versions of themselves, like tree traversal.

The risks are stack overflow when recursion goes too deep (no base case, or a huge input) and the per-call overhead. C compilers may optimize tail calls but don't guarantee it, so deep recursion is often rewritten as a loop.

c
long fact(int n) {
    if (n <= 1) return 1;   /* base case: stops the recursion */
    return n * fact(n - 1); /* each call adds a stack frame */
}

fact(5);   /* 120 */

Q39. How do you read from and write to files in C?

Open a file with fopen, which returns a FILE pointer or NULL on failure. Read and write with fscanf, fprintf, fgets, fread, and fwrite, then The closing step is fclose. Always check the fopen return before using the handle.

Choose the mode carefully: "r" read, "w" truncates or creates, "a" appends, and add "b" for binary on platforms that distinguish it. Forgetting fclose can lose buffered writes, since output is buffered until flush or close.

c
FILE *f = fopen("data.txt", "r");
if (f == NULL) return -1;   /* check before use */

char line[128];
while (fgets(line, sizeof(line), f) != NULL)
    fputs(line, stdout);

fclose(f);

Q40. How are multidimensional arrays laid out, and how do you pass them?

C stores multidimensional arrays in row-major order: all of row 0, then all of row 1, contiguous in memory. So arr[i][j] sits at offset i * cols + j from the base.

When you pass a 2D array to a function, every dimension except the first must be known so the compiler can compute offsets: void f(int a[][COLS], int rows). Iterating in row-major order (outer loop rows, inner loop columns) is also friendlier to the cache.

c
#define COLS 3
void print_grid(int a[][COLS], int rows) {
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < COLS; j++)
            printf("%d ", a[i][j]);
}

int grid[2][COLS] = {{1,2,3},{4,5,6}};
print_grid(grid, 2);
Back to question list

C Interview Questions for Experienced Developers

Experienced20 questions

advanced rounds probe undefined behavior, memory internals, and production scars. Expect every answer here to draw a follow-up.

Q41. What is undefined behavior, and why does it matter so much in C?

Undefined behavior is any operation the C standard leaves with no requirements at all: the compiler may do anything, including work fine today and break after an optimization or compiler upgrade. Examples include dereferencing NULL, signed integer overflow, out-of-bounds access, and use after free.

It matters because compilers optimize assuming UB never happens, so UB can delete your safety checks or reorder code in surprising ways. The senior habit is to treat 'it worked' as no evidence of correctness and to compile with warnings, sanitizers, and defined-behavior discipline.

  • Undefined: no guarantees at all (signed overflow, buffer overrun, use after free).
  • Unspecified: several valid outcomes, compiler picks one (argument evaluation order).
  • Implementation-defined: documented per platform (size of int, char signedness).

Key point: Distinguishing undefined, unspecified, and implementation-defined is a production signal. Most candidates blur all three into 'undefined'.

Watch a deeper explanation

Video: C Programming Full Course for free (Bro Code, YouTube)

Q42. Why is signed integer overflow undefined while unsigned overflow is defined?

Unsigned arithmetic is defined to wrap modulo 2 to the power of the width, so 0xFFFFFFFF + 1 is 0. Signed overflow is undefined, which lets the compiler assume x + 1 > x always holds and optimize loops and bounds checks accordingly.

The practical consequence is that relying on signed wraparound is a real bug even when it seems to work, and a check like if (x + 1 < x) to detect overflow can be optimized away entirely. Use unsigned types or explicit overflow checks (or builtins like __builtin_add_overflow) instead.

c
unsigned u = 0xFFFFFFFFu;
u + 1;    /* 0, defined wraparound */

/* signed overflow is UB; this check may be optimized away: */
int x = INT_MAX;
if (x + 1 < x) { /* compiler may assume this is never true */ }

Q43. How is a running C program laid out in memory?

A typical process image has several segments: text (the read-only machine code), the data segment (initialized globals and statics), the BSS (zero-initialized globals and statics), the heap (grows up as you malloc), and the stack (grows down with function calls).

Knowing this explains real behavior: why globals default to zero (BSS), why a large local array can overflow the stack, why the heap fragments, and why writing to a string literal crashes (it lives in read-only memory).

SegmentHoldsNotes
TextMachine code, string literalsRead-only
DataInitialized globals/staticsRead-write
BSSZero-initialized globals/staticsCleared at start
Heapmalloc allocationsGrows upward, you manage it
StackCall frames, localsGrows downward, automatic

Key point: Connecting a segment to a concrete symptom (string-literal write crashes because text is read-only) is what makes this answer read as experience.

Q44. What does the restrict keyword do?

restrict is a promise from you to the compiler that, for the lifetime of a restrict-qualified pointer, the object it points to is accessed only through that pointer. It rules out aliasing, so the compiler can keep values in registers and reorder loads and stores safely.

It's mainly a performance tool in hot numeric code, like memcpy-style loops, where aliasing would otherwise force conservative reloads. Break the promise (pass overlapping pointers) and you get undefined behavior, so use it only when you can guarantee no overlap.

c
void add(int n, const int *restrict a,
         const int *restrict b, int *restrict out) {
    for (int i = 0; i < n; i++)
        out[i] = a[i] + b[i];  /* compiler assumes no overlap */
}

Q45. What is the strict aliasing rule?

Strict aliasing says the compiler may assume that pointers of incompatible types don't point to the same memory (char pointers are the exception, they may alias anything). This lets it cache values across writes through unrelated pointer types.

The trap is type-punning by casting, say reading a float's bits through an int pointer, which violates the rule and is undefined behavior even though it often appears to work. The defined ways to reinterpret bits are memcpy or a union.

c
/* UB: reading a float through an int* violates strict aliasing */
/* int bits = *(int *)&my_float;  */

/* defined alternative */
float f = 3.14f;
int bits;
memcpy(&bits, &f, sizeof bits);   /* portable type-punning */

Key point: Offering memcpy as the correct type-pun is the payoff. It proves you know the rule exists and how to work with it, not around it.

Q46. How do inline functions differ from macros, and what does inline actually promise?

An inline function is a real function: it has types, a scope, and evaluates each argument once, so it avoids the double-evaluation and precedence traps of macros while still hinting that the body can be pasted at the call site.

The keyword is a hint, not a command; the compiler decides based on its own heuristics and may inline without the keyword or ignore it with it. For performance-critical small helpers, inline functions are the modern replacement for function-like macros.

c
static inline int max_i(int a, int b) {
    return a > b ? a : b;   /* evaluates each arg once, unlike a macro */
}

Q47. What is const correctness and why does it matter in an API?

const correctness means marking every pointer parameter the function won't modify as const (const char *src). It documents the contract, lets callers pass const data, and lets the compiler catch accidental writes.

In a public API it's part of the interface: a function taking const char * promises not to mutate the input, so callers can pass string literals and shared buffers safely. Retrofitting const later tends to ripple through call sites, so senior C code applies it up front.

c
/* the const says: I read src, I never write it */
size_t copy(char *dst, const char *src, size_t n);

Q48. What is memory alignment and when does it matter?

Alignment is the requirement that a type's address be a multiple of its alignment (often its size), because CPUs read aligned data faster and some architectures fault on misaligned access. The compiler aligns variables and pads structs to satisfy it.

It matters when you cast raw bytes to a typed pointer (deserializing a network buffer), use SIMD types that need stronger alignment, or interface with hardware. _Alignas and aligned allocation control it; assuming a char buffer is suitably aligned for an int is a portability bug.

c
printf("%zu\n", _Alignof(double));  /* often 8 */

_Alignas(16) char buffer[64];      /* 16-byte aligned, e.g. for SIMD */

Q49. For multithreaded code, why is volatile not enough, and what replaces it?

volatile only stops the compiler from caching a value; it provides no atomicity and no ordering between threads, so a volatile int shared across threads still has data races and torn reads. It was never a concurrency primitive.

C11 added <stdatomic.h>: _Atomic types and atomic operations with defined memory orderings, plus real synchronization from the threads library or the platform. For shared mutable state across threads, use atomics or a mutex, and reserve volatile for hardware registers and signal handlers.

c
#include <stdatomic.h>
atomic_int counter = 0;
atomic_fetch_add(&counter, 1);  /* atomic, race-free */

/* volatile int counter;  would still race across threads */

Key point: The clean split, volatile for hardware and signals, atomics or a mutex for threads, is exactly the distinction senior interviewers are checking.

Q50. How do buffer overflows lead to security vulnerabilities, and how do you defend against them?

Writing past a buffer's end overwrites adjacent memory. On the stack that can include the saved return address, letting an attacker redirect execution; on the heap it can corrupt allocator metadata. Unbounded copies (gets, strcpy) and off-by-one loops are the usual entry points.

Defenses layer up: bounded functions (snprintf, fgets), validating lengths, compiler and OS protections (stack canaries, ASLR, non-executable stacks), and testing with AddressSanitizer and fuzzing. The mindset is that any input length is adversarial until checked.

Key point: Framing input as adversarial and naming ASan plus fuzzing shows a security posture, which weighs heavily for systems and infrastructure roles.

Q51. What is the difference between a declaration and a definition, and how does the linker use them?

A declaration introduces a name and its type (extern int x; or a function prototype); a definition also allocates storage or provides the body. You can declare a name many times but define it once, the one-definition rule.

The compiler handles one translation unit at a time and leaves external references unresolved; the linker matches each reference to its single definition across object files and libraries. 'Undefined reference' means a definition is missing; 'multiple definition' means the one-definition rule was broken, often a variable defined in a header.

Key point: Explaining that a variable defined (not just declared) in a header causes multiple-definition errors is a debugging story every experienced C dev has lived.

Q52. What is endianness and when do you have to care about it?

Endianness is the byte order a platform uses to store a multibyte value. Little-endian stores the least significant byte first (x86, most ARM), big-endian stores the most significant byte first (network byte order, some older systems).

It matters at boundaries: reading binary files, network protocols, or memory shared between different machines. Within one machine it's invisible. The fix is to serialize with a defined order using htons, htonl, and their inverses, rather than casting raw memory.

c
uint32_t x = 1;
unsigned char *b = (unsigned char *)&x;
/* little-endian: b[0]==1; big-endian: b[3]==1 */

#include <arpa/inet.h>
uint32_t net = htonl(x);  /* to network (big-endian) byte order */

Q53. What is a flexible array member and why use one?

A flexible array member is an array of unspecified length declared as the last member of a struct (int data[];). It lets you allocate a header and its variable-length payload in a single block, sizing the allocation to hold the struct plus the elements.

This beats storing a separate pointer: one allocation, one free, better locality, and no second indirection. It's the standard C99 pattern for variable-length records like packets or strings-with-metadata.

c
struct Packet {
    int len;
    char data[];   /* flexible array member, C99 */
};

struct Packet *p = malloc(sizeof(*p) + n);  /* header + n bytes */
p->len = n;

Key point: Contrasting it with a struct-holding-a-pointer (two allocations, worse locality) is the detail that shows you know why the pattern exists.

Q54. What is the difference between static and dynamic linking?

Static linking copies the library code into your executable at build time: one self-contained binary, larger on disk, and no runtime dependency. Dynamic linking resolves shared libraries at load time (or later): smaller binaries, shared memory pages across processes, and updates without relinking.

The trade-offs are deployment versus flexibility. Static avoids the 'missing .so' problem and version skew; dynamic saves space, lets the OS patch a shared library once, and is what most systems default to. Containers have pushed some teams back toward static for reproducibility.

StaticDynamic
When resolvedAt link timeAt load or run time
Binary sizeLargerSmaller
Runtime dependencyNoneNeeds the shared library present
Library updatesRequires relinkSwap the shared object

Q55. What are setjmp and longjmp, and what are their dangers?

setjmp saves the current execution context into a jmp_buf and returns 0; a later longjmp restores it, causing setjmp to appear to return again with a nonzero value. It's a nonlocal jump, used for a crude form of exception handling across function boundaries.

The dangers are real: it bypasses normal stack unwinding so no cleanup runs, local variables not marked volatile can hold indeterminate values after a longjmp, and jumping into an already-returned function is undefined. Most code should prefer error return codes; setjmp is a last resort.

c
#include <setjmp.h>
jmp_buf env;

if (setjmp(env) == 0) {
    /* first pass */
    longjmp(env, 1);  /* jumps back, setjmp now returns 1 */
} else {
    /* reached after longjmp */
}

Q56. How do compiler optimization levels affect behavior, and why can -O2 expose bugs?

Levels like -O0 through -O3 and -Os trade compile time and code size for speed. -O0 keeps code close to the source for debugging; higher levels reorder, inline, unroll, and eliminate dead code aggressively.

Optimizers assume your program has no undefined behavior, so latent UB (uninitialized reads, strict-aliasing violations, signed overflow) that seemed harmless at -O0 can produce different, wrong results at -O2. When a bug appears only with optimization on, suspect UB first, not the compiler.

Key point: 'A bug that only shows up at -O2 is usually undefined behavior in my code, not a compiler bug' is the mature answer. Blaming the compiler is the junior tell.

Q57. How do you write a generic container in C without templates?

The standard approach is void pointers plus an element size, the way qsort works: the container stores raw bytes, copies elements by size with memcpy, and takes function pointers for operations like comparison or freeing. The caller casts back to the real type on retrieval.

The costs are lost type safety (mistakes surface at run time, not compile time) and an extra indirection. Alternatives are code-generating macros (X-macros or a header included per type) that trade readability for type safety. Naming both approaches and their trade-offs is The production-ready answer.

c
typedef struct {
    void *data;
    size_t len, cap, elem_size;
} Vec;

void vec_push(Vec *v, const void *item) {
    memcpy((char *)v->data + v->len * v->elem_size, item, v->elem_size);
    v->len++;
}

Q58. A C program segfaults intermittently in production. Walk through your debugging approach.

Reproduce with instrumentation first: rebuild with -g and sanitizers (AddressSanitizer for memory errors, UBSan for undefined behavior), which usually pinpoints the exact line for use-after-free, overflow, or a bad pointer. If it only happens live, capture a core dump and open it in gdb to read the crashing stack.

Intermittency points at uninitialized memory, a race, or input-dependent overflow, so widen the input set and run under Valgrind. The structure matters more than any tool: reproduce, instrument, isolate the faulting access, fix, and confirm the fix under the same sanitizer.

Key point: The methodology is (reproduce, instrument, isolate, verify); tools support the evidence.

Watch a deeper explanation

Video: Learn C Programming and OOP with Dr. Chuck [feat. classic book by Kernighan and Ritchie] (freeCodeCamp.org, YouTube)

Q59. What are the key differences between C and C++ that trip people up?

C++ adds classes, templates, RAII, references, function and operator overloading, exceptions, and a large standard library on top of C. Most C code compiles as C++, but not all: C++ is stricter about implicit conversions (notably void* to a typed pointer needs a cast) and treats some C idioms as errors.

The mindset shift is memory management: C is manual malloc/free, while idiomatic C++ ties resource lifetime to object scope with RAII and smart pointers. Bringing C habits (raw new/delete, C-style casts) into C++ is what usually trips experienced C developers.

  • void* to typed pointer: implicit in C, needs a cast in C++.
  • C++ adds RAII, so resources free themselves at scope exit.
  • C++ has references; C only has pointers.
  • bool, references, and stricter type checking differ.

Watch a deeper explanation

Video: C Programming Tutorial - 1 - Introduction (thenewboston, YouTube)

Q60. What are memory barriers and why do you need them in lock-free code?

A memory barrier (fence) constrains the order in which memory operations become visible, preventing the compiler and CPU from reordering loads and stores across it. Modern hardware and optimizers reorder freely for speed, which is fine for single-threaded code but breaks shared-memory assumptions.

In lock-free code you need the right ordering so that, for example, a flag is only seen as set after the data it guards is written. C11 atomics expose this through memory orders (acquire, release, seq_cst); getting them wrong produces bugs that appear only under load on certain CPUs, which is why most code uses locks unless lock-free is truly required.

Key point: Ending with 'use a mutex unless you can prove lock-free is needed' shows judgment. Reaching for memory orders casually is the overconfident answer interviewers distrust.

Back to question list

Why C? C vs C++, Rust, and Python

C wins when you need predictable performance, a tiny runtime, and direct hardware access: kernels, drivers, embedded targets, and anything memory-tight. It trades safety and convenience for control, so you manage memory by hand and the compiler won't catch a dangling pointer. C++ adds abstractions and RAII on top of C's model. Rust keeps the performance while moving memory safety to compile time. Python trades speed for developer velocity. Knowing where each fits, out loud, is itself an interview signal.

LanguageMemory modelBest atWatch out for
CManual (malloc/free)Kernels, drivers, embedded, small runtimesManual memory bugs, undefined behavior
C++Manual + RAII, smart pointersSystems with abstraction, games, enginesLanguage size, long compile times
RustOwnership, checked at compile timeSafe systems code, concurrencySteeper learning curve, borrow checker
PythonAutomatic (garbage collected)Scripting, data, fast prototypingSlower, heavier runtime, less control

How to Prepare for a C Interview

Prepare in layers, and practice out loud. Most C rounds move from concept questions to live coding to a memory or debugging discussion, so rehearse each stage rather than only reading answers.

  • Master your tier's concepts until you can explain them without notes, then read one tier up for the stretch questions.
  • Type and run every snippet with warnings on (gcc -Wall -Wextra); the compiler teaches you as much as any book.
  • Draw memory: stack frames, heap blocks, and where each pointer points. Most C bugs become obvious once you sketch them.
  • Take the quiz on this page to find weak spots, then revisit those questions before the real round.

The typical C interview flow

1Recruiter or phone screen
background, motivation, a few concept checks
2Language and memory concepts
pointers, the stack vs heap, storage classes, undefined behavior
3Live coding
write and explain a function, often pointer or string manipulation
4Debugging or design
spot a leak or overflow, read unfamiliar code, follow-ups

Earlier rounds increasingly run as AI coding interviews. The concepts on this page are what gets probed at every stage.

Test Yourself: C Quiz

Ready to test your C knowledge?

10 questions, about 6 minutes. Score 70% or higher to earn a shareable certificate.

10 questions Instant feedback Free certificate on 70%+

Frequently  Asked  Questions

Which C topics should I prioritize before a technical round?

Prioritize the areas that change real decisions: fundamentals, practical tasks, debugging, trade-offs, and evidence. The highest-value questions are tied to a project example, a validation check, or a failure mode.

Are these questions enough to pass a C interview?

They cover the question-answer portion well, but most C rounds also include live coding: writing a function under observation while explaining your thinking, often something with pointers or strings. solving small problems out loud with a timer is the practice path. Our AI coding interview prep guide covers that format, including how automated evaluation reads your process.

Which C standard do these answers assume?

C99 through C11, the versions in everyday use. Where a feature is newer (like variable-length arrays in C99 or _Generic in C11) the answer says so. If an interviewer targets embedded work on an older compiler, mention that you'd check the target standard first; that awareness reads well.

How long does it take to prepare for a C interview?

If you use C at work or in study, one to two weeks of an hour a day covers this bank with practice runs. Starting colder, plan three to four weeks and write code daily; reading answers without compiling them is how preparation quietly fails, because C punishes small mistakes.

Do interviewers ask exactly these questions?

The concepts repeat far more reliably than the exact wording. Interviewers rephrase, chain follow-ups, and push toward whatever you claim confidence in. Prepare the underlying ideas, pointers, memory layout, undefined behavior, and the phrasing takes care of itself.

Is there a way to test my C knowledge?

Yes. The quiz on this page scores you as you go and explains every answer, so you find your weak spots fast. Pass the threshold and you can download a shareable certificate with your name and score, free and with no sign-up. It's the quickest way to check yourself before the real round.

From the team that builds coding interviews

Hyring builds the AI Coding Interviewer that runs live technical rounds in 20+ languages for 5,000+ hiring teams. These C questions and scoring notes reflect what actually gets asked and evaluated inside the interviews we host.

See how the AI Coding Interviewer works

Sources

Adithyan RKWritten by Adithyan RK
Surya N
Fact-checked by Surya N
Published on: 1 May 2026Last updated: 10 Jul 2026
Share: