C Interview Questions and Answers
20 hand-picked C interview questions with
detailed answers. Open the interactive version above to search, filter
by difficulty, run code, bookmark questions and track your progress.
What is C and what is it used for?
C is a procedural, compiled, low-level language created in 1972. It provides direct memory access via pointers and maps closely to machine operations.
Used for: operating systems (Linux kernel), embedded systems, device drivers, databases (PostgreSQL, Redis), language runtimes (Python interpreter, JVM), and performance-critical libraries.
What is a pointer in C?
A pointer is a variable that stores the memory address of another variable. Declared with *: int *p;
&x — address-of operator (get address of x).*p — dereference (access value at address).
Pointers enable dynamic memory, arrays, strings, function callbacks, and efficient parameter passing by reference.
int x = 42;
int *p = &x; // p holds address of x
printf("%d", *p); // 42
malloc, calloc, realloc, and free.
- malloc(size) — allocate uninitialized bytes on the heap. Returns void* or NULL on failure.
- calloc(n, size) — allocate and zero-initialize n elements.
- realloc(ptr, new_size) — resize existing allocation (may move memory).
- free(ptr) — release heap memory. Must match every malloc/calloc/realloc.
Heap memory persists until freed — unlike stack variables that die when the function returns.
int *arr = malloc(10 * sizeof(int));
if (arr == NULL) { /* handle error */ }
// use arr...
free(arr);
arr = NULL;
Stack vs heap memory.
| Stack | Heap |
|---|
| Allocation | Automatic (local variables) | Manual (malloc/free) |
| Lifetime | Function scope | Until freed |
| Speed | Very fast | Slower |
| Size | Limited (~1-8 MB) | Limited by RAM |
| Fragmentation | None | Possible |
int* bad() {
int x = 5;
return &x; // BUG: x is on the stack
}
Relationship between arrays and pointers.
An array name decays to a pointer to its first element in most contexts. arr[i] is equivalent to *(arr + i).
Key differences: sizeof(arr) gives total array size; sizeof(ptr) gives pointer size (8 bytes on 64-bit). Arrays can't be reassigned; pointers can.
void print(int *arr, int len) {
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
}
How are strings represented in C?
C has no built-in string type. Strings are null-terminated char arrays — the '\0' character marks the end.
Use string.h: strlen, strcpy, strncpy (safer), strcmp, strcat. Always ensure the destination buffer is large enough.
char name[50];
strncpy(name, "Alice", sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
What is a struct?
A struct groups related variables under one type. Members are stored contiguously in memory (with possible padding for alignment).
Access with dot notation (s.field) or arrow (ptr->field) for pointers to structs.
struct Point { int x; int y; };
struct Point p = {10, 20};
struct Point *ptr = &p;
printf("%d", ptr->x);
What are function pointers?
A variable that stores the address of a function. Syntax: return_type (*name)(param_types).
Used for: callbacks (qsort comparator, signal handlers), plugin architectures, state machines, and implementing polymorphism-like behavior in C.
int add(int a, int b) { return a + b; }
int (*op)(int, int) = add;
printf("%d", op(3, 4)); // 7
C preprocessor directives.
#include — paste header file contents (<> for system, "" for local).#define — text substitution / macros.#ifdef / #ifndef / #endif — conditional compilation (header guards).#pragma once — non-standard but widely supported header guard.
Macros are processed before compilation — no type checking.
#ifndef MY_HEADER_H
#define MY_HEADER_H
// declarations
#endif
static, extern, const, and volatile.
- static (variable) — persists between function calls; file scope if at file level.
- extern — declares a variable defined elsewhere (shared across files).
- const — read-only; compiler may place in read-only memory.
- volatile — tells compiler the value may change unexpectedly (hardware registers, ISR). No optimization caching.
static int count = 0; // persists between calls
void increment() { count++; }
What is undefined behavior in C?
Behavior the C standard does not define — the program can do anything (crash, wrong result, appear to work). Common sources:
- Dereferencing NULL or dangling pointers
- Signed integer overflow
- Buffer overflow / out-of-bounds access
- Use after free
- Uninitialized variables
- Modifying a string literal
char *s = "hello";
s[0] = 'H'; // UB: string literals are read-only
Bitwise operators in C.
& AND — mask bits| OR — set bits^ XOR — toggle bits~ NOT — flip all bits<< left shift — multiply by 2^n>> right shift — divide by 2^n
Used for: flags, embedded registers, fast arithmetic, permission bitmasks.
#define FLAG_READ 0x01
#define FLAG_WRITE 0x02
int perms = FLAG_READ | FLAG_WRITE;
if (perms & FLAG_READ) { /* can read */ }
Recursion in C — how does it work?
A function calls itself with a smaller subproblem until a base case stops recursion. Each call pushes a new stack frame.
Risks: stack overflow on deep recursion (limited stack size). Tail recursion may be optimized by the compiler but isn't guaranteed in C.
int factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1);
}
File I/O in C.
Use FILE* from stdio.h:
fopen(path, mode) — open ('r', 'w', 'a', 'rb').fread/fwrite — binary I/O.fgets/fprintf — text I/O.fclose — always close; flush buffers.
Check return values — fopen returns NULL on failure.
FILE *f = fopen("data.txt", "r");
if (!f) { perror("fopen"); return -1; }
char buf[256];
while (fgets(buf, sizeof(buf), f)) printf("%s", buf);
fclose(f);
Implement a singly linked list node in C.
A node is a struct with data and a pointer to the next node. The list is a pointer to the head node.
Operations: insert (head/tail), delete, search, traverse. Memory must be manually managed with malloc/free.
struct Node {
int data;
struct Node *next;
};
void push_front(struct Node **head, int val) {
struct Node *n = malloc(sizeof(struct Node));
n->data = val;
n->next = *head;
*head = n;
}
enum and typedef.
enum — named integer constants. Compiler assigns sequential values unless specified. Improves readability over magic numbers.
typedef — creates an alias for an existing type. Simplifies complex types (especially function pointers and structs).
typedef enum { IDLE, RUNNING, STOPPED } State;
typedef struct Node Node;
struct Node { int data; Node *next; };
sizeof and struct padding.
sizeof returns the size in bytes of a type or variable. sizeof(arr)/sizeof(arr[0]) gives array length.
Structs may have padding — the compiler inserts unused bytes so members align to their natural boundaries (e.g. int on 4-byte boundary). Reordering members can reduce struct size.
struct Bad { char a; int b; }; // likely 8 bytes (3 padding)
struct Good { int b; char a; }; // likely 8 bytes (3 padding at end)
What is a void pointer?
void* is a generic pointer that can point to any data type. Cannot be dereferenced directly — must be cast to the correct type first.
Used in: malloc (returns void*), qsort/bsearch callbacks, generic data structures, and low-level system programming.
void *generic = malloc(100);
int *ints = (int *)generic;
free(generic);
Why do header files need include guards?
Without guards, including a header twice causes duplicate definition errors. Include guards prevent re-processing:
#ifndef FILE_H / #define FILE_H / ... / #endif or #pragma once.
Headers declare (function prototypes, types); source files (.c) define (implementations).
What causes a segmentation fault and how do you debug it?
A segfault occurs when a program accesses memory it doesn't have permission to access. Common causes:
- NULL pointer dereference
- Buffer overflow
- Use after free
- Stack overflow (deep recursion)
- Writing to read-only memory
Debug with: gdb (core dumps), Valgrind (memory errors), AddressSanitizer (-fsanitize=address).
gcc -g -Wall -fsanitize=address -o app app.c
gdb ./app
# run, then: bt (backtrace on crash)