1. What occurs if calloc() fails to allocate requested memory blocks?
Answer: It returns NULL.
All C dynamic-allocation functions — malloc, calloc, realloc — signal failure by returning NULL; none of them throw or abort. calloc returns NULL when it can’t allocate (or when num * size overflows, in which case it also returns NULL).
So the caller must check the result:
int *p = calloc(n, sizeof(int));
if (p == NULL) {
/* handle allocation failure */
}
This is why every allocation in C should be followed by a null check. The interview answer: calloc returns NULL on failure, which the caller must check.
2. What is the purpose of string function strcspn(s1, s2)?
Answer: It returns the length of the initial segment of s1 consisting of characters NOT in s2.
strcspn(s1, s2) scans s1 from the start and counts characters until it hits one that appears in s2:
strcspn("hello world", " ") // 5 — stops at the space
strcspn("abc123", "xyz") // 6 — no x/y/z found, counts all of s1
Think “complement span not” — the span of s1 that is the complement of (not in) s2. Its sibling strspn(s1, s2) counts the initial segment consisting only of characters in s2. Use cases: finding the first occurrence of any of a set of characters (the return value is the index), parsing delimiters. The interview answer: the length of s1’s prefix that contains no characters from s2.
3. What is the size of char type guaranteed by the C standard on all compliant platforms?
Answer: Exactly 1 byte (a byte being CHAR_BIT bits, typically 8).
By definition, sizeof(char) is 1 on every conforming C implementation — it’s the unit in which all other sizes are measured. The subtlety: the number of bits in a byte is CHAR_BIT, which the standard allows to vary (usually 8; older exotic DSPs had more). So:
sizeof(char) == 1always.CHAR_BITgives how many bits that byte holds (typically 8, could be 16, 32, etc. on unusual platforms).
char, signed char, unsigned char all occupy 1 byte each (but are three distinct types). The interview answer: exactly 1 byte (CHAR_BIT bits, normally 8) on all compliant platforms.
4. What is the result of using sizeof on a variable length array (VLA) parameter inside a function void foo(int n, int arr[n])?
Answer: sizeof(arr) gives the pointer size (e.g., 8 bytes on 64-bit), because the VLA parameter decays to a pointer.
A VLA parameter (int arr[n]) is not a real VLA — function parameters of array type always decay to pointers. void foo(int n, int arr[n]) is adjusted to void foo(int n, int *arr). Inside foo, arr is a pointer, so sizeof(arr) is the pointer size (8 on 64-bit), not n × sizeof(int).
True VLAs (where sizeof does evaluate at runtime) only exist for local/block-scope arrays with non-constant size:
void bar(int n) {
int local[n]; // real VLA
sizeof(local); // runtime n * sizeof(int)
}
The interview answer: pointer size — VLA parameters decay to int *, so sizeof(arr) is 8 bytes, not the array’s byte size.
5. What does the file mode string “a+” specify when using fopen()?
Answer: Open for both reading and appending — existing content is preserved and all writes go to the end of the file.
"a+" (append + read/update) means:
- The file is opened for reading and writing.
- Existing content is preserved (unlike
"w"which truncates). - The file is created if it doesn’t exist.
- All write operations are forced to the end of the file — even if you
fseekthe position indicator elsewhere, writes still append at the end (the position indicator moves to end before each write). Reads can seek anywhere.
Compare the modes:
"w"— write, truncates to zero length."r"— read only."a"— append only (write-only)."a+"— append + read.
The interview answer: read+append mode — content preserved, writes always land at the file’s end.
6. What is the purpose of va_start, va_arg, and va_end macros in <stdarg.h>?
Answer: They manage variadic (variable-argument) function parameter lists — functions that take a variable number of arguments, like printf.
A variadic function is declared with ...: int sum(int count, ...). To access the variable arguments:
#include <stdarg.h>
int sum(int count, ...) {
va_list ap;
va_start(ap, count); // initialize after the last fixed param
int total = 0;
for (int i = 0; i < count; ++i)
total += va_arg(ap, int); // fetch next arg as int
va_end(ap); // cleanup
return total;
}
va_start(ap, last)— begins iteration, with the last named parameter.va_arg(ap, type)— returns the next argument converted totype(types must be promoted:intforchar/shortargs,doubleforfloat).va_end(ap)— must be called before the function returns.
There’s no type safety — the callee must know the types (via the fixed args or format string), and printf-style format mismatch is UB. The interview answer: the <stdarg.h> macros for walking a variable argument list (va_start/va_arg/va_end).
7. What happens if a recursive function in C lacks a valid base case?
Answer: Infinite recursion — each call pushes a new stack frame until the stack limit is exhausted, causing a stack overflow crash.
Every recursive call allocates a stack frame (return address, saved registers, locals). Without a terminating base case, the recursion never bottoms out, frames accumulate until the thread’s fixed stack memory is consumed, and the program faults (typically SIGSEGV/stack overflow).
void recurse() { recurse(); } // no base case → stack overflow
Even with a base case, deeply nested recursion can still overflow on small stacks. The fix: ensure every recursive path reaches a base case, or convert to iteration for unbounded/very deep work. The interview answer: unbounded recursion exhausts the call stack and crashes with a stack overflow.
8. What is the value of NULL in standard C header files?
Answer: An implementation-defined null pointer constant — typically ((void*)0) or 0.
NULL is a macro (defined in <stddef.h>, <stdio.h>, etc.) representing a null pointer constant. Common definitions:
((void *)0)— a void pointer to address 0.0— the integer zero constant, which converts to a null pointer in pointer contexts.
Either way, NULL compares equal to any null pointer and is not a valid address to dereference. The standard requires a null pointer constant to be an integer constant expression with value 0, or such an expression cast to void*. In C++, the idiom is nullptr (since C++11). The interview answer: an implementation-defined null pointer constant, commonly ((void*)0) or plain 0.
9. What is the result of bitwise operation 1 << 31 on a 32-bit signed int variable in C?
Answer: Undefined behavior — the shift moves a 1 into the sign-bit position, which overflows a signed int.
Left-shifting a signed value is well-defined only if the result is representable in the signed type. 1 << 31 on a 32-bit int produces 0x80000000 — a value whose bit pattern is the sign bit set, i.e., not representable as a positive int. Per the C standard that’s undefined behavior (pre-C23 rules).
The safe way to get the high bit is to use an unsigned type: 1u << 31 (or 1UL << 31) — well-defined, gives 0x80000000 = 2147483648. The interview answer: UB — shifting into the sign bit of a signed int; use an unsigned type instead.
10. What does the string conversion function strtol() provide compared to atoi()?
Answer: strtol() provides error detection (invalid input, overflow via errno) and arbitrary base conversion; atoi() is a bare parser with no error reporting.
atoi(str)— converts toint, but on failure returns0with no way to distinguish “the string was"0"” from “invalid input,” and no overflow detection.strtol(str, &endptr, base)— full-featured:- Error detection: sets
errnotoERANGEon overflow/underflow and returnsLONG_MAX/LONG_MIN;endptrpoints past the consumed digits, so you can verify the whole string was parsed (*endptr == '\0'). - Base selection:
base2–36 (hex0x, octal0, decimal10, or0= auto-detect by prefix).
- Error detection: sets
char *end;
errno = 0;
long v = strtol(s, &end, 10);
if (errno == ERANGE) /* overflow */;
if (end == s) /* no digits consumed */;
The interview answer: strtol detects errors (via errno/endptr) and supports arbitrary bases; atoi has neither.
11. What is the purpose of alignment macro alignof / _Alignof introduced in C11?
Answer: It queries the alignment requirement (in bytes) of a specified type.
alignof(type) (or its spelling _Alignof; <stdalign.h> provides the alignof macro) returns the byte alignment that objects of that type must have:
alignof(char) // typically 1
alignof(int) // typically 4
alignof(double) // typically 8
This is the natural alignment the compiler enforces when placing the type in memory (e.g., a double at offset multiple of 8). C11 also added _Alignas (declaration specifier to request alignment) and aligned_alloc (allocate with a given alignment). These matter for hardware, vector types, and ABI-compatible struct layouts. The interview answer: alignof(type) returns the type’s required byte alignment.
12. What happens when calling fclose() on an open file stream in C?
Answer: It flushes buffered data to the file, closes the OS file descriptor, and releases the I/O buffers.
fclose(stream) performs the full teardown:
- Flushes any unwritten buffered output to the underlying file (like
fflush). - Closes the operating-system file descriptor.
- Frees the stream’s internal I/O buffer memory.
After fclose, the stream is invalid — using it further is undefined behavior. Note: the file itself is not deleted (that’s remove()); and failing to fclose can lose buffered output and leak file descriptors. The interview answer: flush + close descriptor + free stream buffers; the file remains on disk.
13. What does ptrdiff_t represent in <stddef.h>?
Answer: A signed integer type used to hold the result of subtracting two pointers into the same array.
ptrdiff_t is the result type of pointer subtraction:
int arr[10];
int *a = &arr[2], *b = &arr[7];
ptrdiff_t d = b - a; // 5 — the element distance
- It’s signed (can be negative).
- It measures the distance in array elements, not bytes.
- Defined in
<stddef.h>(C) /<cstddef>(C++). - The subtraction is only well-defined when both pointers point into the same array (or one past the end).
It’s typically as wide as a pointer (e.g., 64-bit signed on 64-bit platforms). The interview answer: the signed result type of subtracting two pointers into the same array — the element distance.
14. What happens if malloc() is requested to allocate memory larger than available system memory?
Answer: malloc() fails and returns NULL — the program does not crash automatically.
When the allocator can’t satisfy the request (system memory exhausted, or the request size itself is absurd), malloc returns NULL rather than crashing or throwing. What happens next is up to the caller:
- A well-written program checks for
NULLand handles the failure gracefully. - A careless program dereferences the
NULL→ crash (segmentation fault).
Note an extra wrinkle: on many OSes, malloc can succeed initially thanks to overcommit (memory is committed lazily on touch), and the failure shows up as an OOM kill or fault later. But at the API level, exhaustion → NULL. The interview answer: malloc returns NULL to signal allocation failure; the caller must check it.
15. What is the primary operational issue with static variables declared inside functions in multithreaded C programs?
Answer: A function-local static variable is a single shared instance across all threads — concurrent access with a write causes data races unless synchronized.
Despite being declared inside a function, a static local lives in the program’s data segment, shared by every thread that calls the function. If two threads call the function and both read/write that variable without synchronization, that’s a data race (undefined behavior in C11’s memory model) — corrupted or inconsistent values.
int next() {
static int counter = 0; // ONE copy for all threads
return counter++; // race if called concurrently
}
Fixes: protect with a mutex, use C11 atomics (_Atomic), or make it thread-local (C11 _Thread_local/thread_local) if each thread should have its own instance. The interview answer: the static is shared by all threads, causing data races on concurrent access unless mutex/atomic/thread-local is used.
16. What is the effect of applying logical OR || operator on boolean expressions in C?
Answer: Short-circuit evaluation — if the left operand is true (non-zero), the right operand is skipped.
|| evaluates left to right and stops as soon as the result is known:
- If the left operand is non-zero (true), the whole expression is true regardless of the right side — so the right operand is not evaluated (no side effects from it).
- Only if the left is
0(false) is the right operand evaluated.
if (p != NULL && *p) { } // && short-circuits too
if (flag || expensive()) { } // expensive() skipped if flag true
This is the basis of safe idioms like ptr != NULL && ptr->field (the second operand only runs if ptr is valid). && is the mirror image: right side skipped when the left is false. The interview answer: || short-circuits — the right operand is skipped when the left is true.
17. What does offsetof(type, member) in <stddef.h> calculate?
Answer: The byte offset of a structure member from the start of the struct, accounting for padding.
offsetof(struct_type, member) returns the number of bytes from the beginning of the struct to that member, including any padding the compiler inserted for alignment:
struct Data { char a; double b; int c; };
offsetof(struct Data, b) // 8 (7 padding bytes after a)
offsetof(struct Data, c) // 16
Uses: manual serialization, building generic reflection/field tables, allocating flexible layouts. It’s a compile-time constant (works in static contexts and constant expressions). The interview answer: the padded byte offset of a member within its struct.
18. What is the result of applying sizeof(“Hello”) in C?
Answer: 6 — the 5 characters plus the implicit null terminator '\0'.
A string literal is stored as an array of char including a terminating '\0'. "Hello" is {'H','e','l','l','o','\0'} — 6 bytes. So sizeof("Hello") is 6.
The common trap: strlen("Hello") returns 5 (it counts up to, but not including, the null terminator). The difference between sizeof and strlen for literals is exactly the +1 for the terminator. The interview answer: 6 — the 5 characters plus the null terminator.
19. What is the output of integer division -7 / 3 in C99 and newer standards?
Answer: -2 — truncation toward zero.
-7 / 3 is -2.333.... C99 standardized integer division to truncate toward zero:
7 / 3 = 2,-7 / 3 = -2(the fractional part is discarded, moving toward zero, not toward negative infinity).
Pre-C99, the behavior was implementation-defined (some old compilers truncated toward negative infinity, giving -3). Since C99 it’s defined: toward zero. So -7 % 3 is -1 (the sign follows the dividend, and a = (a/b)*b + a%b holds). The interview answer: -2 — C99 truncates toward zero.
20. What does the storage class extern signify when applied to a variable declaration inside a function frame (extern int count;)?
Answer: It declares that count refers to a global variable defined elsewhere — it does not allocate new storage.
extern is a declaration without definition: it tells the compiler “this name refers to storage that exists elsewhere (another file, or a global scope) — don’t allocate memory for it here.” Inside a function, extern int count; lets you reference a file-scope global by name without creating a local copy:
int count; // global definition in file1.c
// file2.c
void f() {
extern int count; // refers to file1.c's global, no new memory
count++;
}
Contrast a plain local int count; — that allocates a new automatic variable shadowing the global. extern in block scope simply links the name to the external definition, and prevents a separate local allocation. The interview answer: extern references storage defined elsewhere (a global in another scope/file) without allocating new memory.
Premium Content
Unlock Top 50 - Part 3 and all premium lessons with a subscription.
From ₹199.99/year — See plans