interviewDeck

Your one-stop platform to prepare, practice and ace your interviews.

Loading your questions…

All Questions

Filters & tools

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.

StackHeap
AllocationAutomatic (local variables)Manual (malloc/free)
LifetimeFunction scopeUntil freed
SpeedVery fastSlower
SizeLimited (~1-8 MB)Limited by RAM
FragmentationNonePossible
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)