Commonly Asked C Technical Interview Questions (with Answers)
The seven concept areas C interviewers test most: pointers, memory, strings, storage classes, preprocessor, structs, and undefined behavior, with worked examples.
C technical interviews test seven concept areas, and candidates who cover all seven rather than only their strongest two advance past shortlisting far more consistently.
The seven areas: pointers, memory management, strings, storage classes, the preprocessor, structs and unions, and standard library quirks. Each section below gives the question, the answer, and the worked example interviewers use to verify the answer.
Pointers — the three variants that appear in shortlisting rounds
Pointer questions at shortlisting are almost always output-prediction, not coding. Three variants appear across the widest range of placement tests.
Pointer to pointer
A pointer-to-pointer stores the address of a pointer variable.
#include <stdio.h>
int main(void) {
int x = 42;
int *p = &x;
int **pp = &p;
printf("%d\n", **pp); /* 42 */
**pp = 99;
printf("%d\n", x); /* 99 */
return 0;
}
pholds the address ofx.ppholds the address ofp.**ppdereferences twice: first to reachp, then to reachx.
Pointer-to-pointer is used in scanf for string input, in dynamic 2-D arrays, and in linked-list head-pointer updates where the caller’s pointer must change.
Function pointers
Function pointers let you store and call a function via a variable. They are the mechanism behind callbacks and dispatch tables.
#include <stdio.h>
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
int main(void) {
int (*fp)(int, int); /* fp: pointer to function(int,int)->int */
fp = add;
printf("%d\n", fp(3, 4)); /* 7 */
fp = mul;
printf("%d\n", fp(3, 4)); /* 12 */
return 0;
}
The declaration int (*fp)(int, int) binds the * to fp before the argument list, making fp a pointer rather than a function that returns int *. Interviewers commonly swap the parentheses in MCQs to test whether candidates catch the difference.
Pointer to array vs array of pointers
int arr[5] = {1, 2, 3, 4, 5};
int (*pa)[5] = &arr; /* pointer to an array of 5 ints */
int *ap[5]; /* array of 5 pointers to int */
sizeof(*pa) returns 20 (size of the whole array). sizeof(ap) returns 40 on a 64-bit system (5 pointers at 8 bytes each). The parentheses decide which interpretation the compiler uses.
For the full pointer-decay mechanics and why arr[i] equals *(arr + i), see Pointers and Arrays in C.
Memory management — stack, heap, and the four allocation functions
Stack memory is automatic: allocated when a function is entered, released when it returns. Heap memory is manual: you request it with an allocator and are responsible for releasing it with free.
malloc, calloc, realloc, and free
Per the cppreference C memory reference, the four allocation functions work as follows:
| Function | What it does | Initialisation |
|---|---|---|
malloc(size) | Allocates size bytes | None (bytes contain prior data) |
calloc(n, size) | Allocates n elements of size bytes | Zero-initialises all bytes |
realloc(ptr, new_size) | Resizes a prior allocation | Preserves original bytes |
free(ptr) | Returns memory to the heap | None |
All three allocators return void * on success and NULL on failure. Always check the return value before using the pointer.
Memory leaks and dangling pointers
A memory leak happens when heap memory is allocated but never freed before the pointer goes out of scope.
void leak_example(void) {
int *p = malloc(100 * sizeof(int));
/* use p ... */
/* forgot free(p): 400 bytes leak on every call */
}
A dangling pointer holds an address that is no longer valid. The fix: assign p = NULL immediately after free(p). free(NULL) is a safe no-op; calling free on the same non-null address twice is undefined behavior.
Common error patterns and the code structures that prevent them are catalogued in Common Errors in C Programming.
Strings, storage classes, and the preprocessor
Strings and the strcpy vs strncpy trap
C strings are character arrays terminated by a null byte ('\0'). Every string function depends on finding that terminator.
strcpy(dest, src) copies bytes until it hits '\0' in src, with no length check on dest. If src is longer than the buffer, strcpy writes past the end: a classic buffer overflow.
strncpy(dest, src, n) copies at most n bytes. If src fills all n bytes without a '\0', dest will not be null-terminated. Always add dest[n-1] = '\0' after strncpy to guarantee safe termination.
Storage classes
C has four storage-class specifiers: auto, register, static, and extern.
| Specifier | Lifetime | Linkage | Notes |
|---|---|---|---|
auto | Block (stack) | None | Default for local variables |
register | Block (stack) | None | Hints register allocation; & forbidden |
static (local) | Program lifetime | None | Retains value across calls |
static (file scope) | Program lifetime | Internal | Visible only in current translation unit |
extern | Program lifetime | External | Declares a symbol defined elsewhere |
Output-prediction test for static local variable:
#include <stdio.h>
void counter(void) {
static int count = 0;
count++;
printf("Count: %d\n", count);
}
int main(void) {
counter(); /* Count: 1 */
counter(); /* Count: 2 */
counter(); /* Count: 3 */
return 0;
}
count is initialised once to 0, then incremented on every call. The value persists across calls because static storage outlives the function’s stack frame.
Preprocessor: #define vs const and macros vs functions
#define MAX 100 is a textual substitution performed before compilation. MAX has no type, no address, and no scope. const int MAX = 100 is a typed constant that obeys normal scoping rules. Prefer const or enum for integer constants in C, because they participate in type checking.
Macros look like functions but are not. The classic side-effect pitfall:
#define SQUARE(x) ((x) * (x))
int i = 3;
int result = SQUARE(i++); /* expands to ((i++) * (i++)) — evaluates i++ twice */
Evaluating i++ twice in the same expression is undefined behavior under C11. An inline function evaluates its argument once and avoids this entirely.
Structs, unions, and standard library quirks
Struct alignment and padding
The compiler inserts padding bytes between struct members to satisfy alignment requirements.
struct Example {
char c; /* 1 byte */
/* 3 bytes padding on most 32/64-bit platforms */
int n; /* 4 bytes */
};
/* sizeof(struct Example) == 8, not 5 */
Reordering members from largest to smallest minimises padding. This is a common output-prediction question: “What does sizeof return for this struct?”
Union size rule
In a union, all members share the same starting address and the size equals the size of the largest member.
union Data {
int i; /* 4 bytes */
float f; /* 4 bytes */
char c; /* 1 byte */
};
/* sizeof(union Data) == 4 */
Writing u.i = 10 then reading u.f is undefined behavior. Only the most-recently-written member may be read safely. Use a struct when all fields are needed simultaneously; use a union when you need multiple interpretations of the same bytes.
sizeof, NULL, and undefined behavior
sizeof is a compile-time operator, not a function. sizeof(int) and sizeof(some_variable) both resolve before the program runs. The key implication: sizeof(arr) inside a function that received arr as a pointer returns 8 on a 64-bit system (pointer size), not the original array size.
NULL is a null-pointer constant, defined most commonly as ((void *)0) or 0. It is defined in <stddef.h> (and re-exported by <stdio.h>, <stdlib.h>, and others). Comparing a pointer to NULL with == is defined; dereferencing NULL is not.
The SEI CERT C Coding Standard catalogues the most common C undefined behaviors interviewers draw from: signed integer overflow, out-of-bounds pointer arithmetic, aliasing violations, and double-free. These appear in placement tests because they produce surprising output on optimised builds, not just debug builds.
For deeper treatment of undefined behavior and the output questions it generates, see Must-Solve Conceptual C Programming Questions. For program-writing practice across pointers, memory, and data structures, see 31 Most-Asked C Programming Interview Questions.
C’s memory model (manual allocation, pointer arithmetic, strict-aliasing rules) is the same model that runs underneath inference runtimes and OS kernels. Candidates who build that mental model during placement prep have a shorter path to understanding how LLM inference engines manage tensor memory. TinkerLLM’s tracks connect these layers; the entry point is ₹299 at tinkerllm.com.
Primary sources
Frequently asked questions
What is a dangling pointer and how do you avoid it?
A dangling pointer holds the address of memory that has been freed or gone out of scope. Dereferencing it is undefined behavior. The fix: set the pointer to NULL immediately after calling free() so that any accidental dereference produces a predictable crash rather than silent data corruption.
What is the difference between malloc and calloc in C?
malloc(size) allocates a block of the given byte count without initialising the memory — the bytes contain whatever was previously stored there. calloc(n, size) allocates n elements of the given element size and zero-initialises all bytes. Use calloc when your code depends on zero-initial state; use malloc when you will fill every byte before reading.
What is a function pointer and how do you declare one in C?
A function pointer stores the address of a function with a specific signature. Declaration: int (*fp)(int, int) declares fp as a pointer to a function that takes two ints and returns int. Assign with fp = myFunc; and invoke with fp(a, b) or (*fp)(a, b). Function pointers are used in callbacks, dispatch tables, and plugin systems.
What is the difference between strcpy and strncpy?
strcpy(dest, src) copies the null-terminated source into dest with no length limit — if src is longer than the buffer, it writes past the end (buffer overflow). strncpy(dest, src, n) copies at most n bytes, but if src fills all n bytes without a null byte, dest will not be null-terminated. Always write dest[n-1] = '\0' explicitly after strncpy to guarantee termination.
What does the static keyword do when applied to a local variable?
A static local variable is initialised once at program start and retains its value across all calls to its enclosing function. The storage lives in the BSS or data segment, not on the stack. This differs from a static global variable, which restricts the global's visibility to the current translation unit.
What is the difference between a struct and a union in C?
In a struct, each member has its own memory region and the total size is at least the sum of member sizes plus alignment padding. In a union, all members share the same memory region and the size equals the size of the largest member. Use a struct when you need all fields simultaneously; use a union to overlay multiple interpretations on the same bytes.
What is undefined behavior in C and why does it matter in interviews?
Undefined behavior means the C standard places no requirement on what the program does. Common examples: signed integer overflow, dereferencing a null or dangling pointer, reading an uninitialised variable, calling free() twice. Compilers may optimize away UB branches, producing output that surprises candidates who expect a predictable crash. Interviewers use UB snippets to distinguish candidates who understand the standard from those who guess.
Why is sizeof not a function in C?
sizeof is a compile-time operator. The compiler replaces sizeof(type) with the byte count before the program runs and does not evaluate its operand at runtime (except for C99 variable-length arrays). The key implication: sizeof(arr) inside a function that received arr as a pointer returns the pointer size, not the original array size.
A self-paced playground for building with LLMs.
TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.
Try TinkerLLM (₹299 launch)