C Fundamentals
1. Why is C still relevant?
Operating systems (Linux, Windows kernel), embedded systems, microcontrollers, databases (Redis, SQLite), language runtimes (CPython, Lua) — all written in C. It compiles to small, fast machine code with no runtime.
2. ++i vs i++ ?
++i— pre-increment; increments, then returns the new value.i++— post-increment; returns the old value, then increments. In a stand-alone statementi++;and++i;are identical. Difference matters in expressions:int a = i++vsint a = ++i.
3. malloc vs calloc vs realloc vs free?
malloc(n)— allocate n uninitialised bytes.calloc(n, size)— allocate n*size bytes, zero-initialised.realloc(p, n)— resize allocation (may copy + return new pointer).free(p)— release. Callingfreetwice on the same pointer is undefined behaviour.
4. What is a dangling pointer?
A pointer to memory that has been freed or has gone out of scope.
int *p;
{
int x = 10;
p = &x;
}
*p; // dangling — x no longer exists
Pointers
5. Pointer arithmetic — what does p + 1 do?
Advances by sizeof(*p) bytes, NOT 1 byte. For int *p, p + 1 is 4 bytes ahead on most systems.
6. *p++ vs (*p)++ vs *++p?
*p++— dereference p, THEN increment p.(*p)++— dereference, get value, increment the VALUE.*++p— increment p first, then dereference.
7. What is the difference between char *s = "hello" and char s[] = "hello"?
char *s—spoints to a string literal in read-only memory. Modifying it is undefined behaviour.char s[]— array of chars on the stack, initialised by copy. Modifying is safe.
8. NULL pointer vs uninitialised pointer?
- NULL — explicitly points to "nothing". Dereferencing crashes deterministically (usually).
- Uninitialised — points to garbage. Dereferencing is undefined behaviour — could crash, could corrupt random memory, could "work".
Memory & Storage
9. Stack vs heap?
| Stack | Heap | |
|---|---|---|
| Allocation | Automatic (local vars) | Manual (malloc/free) |
| Size | Small (8 MB typical) | Large |
| Speed | Very fast | Slower |
| Lifetime | Until function returns | Until free |
| Fragmentation | Never | Yes |
10. What is a memory leak?
Allocating memory and never freeing it. In a long-running program (server, daemon), accumulated leaks eventually exhaust RAM and crash. Detect with valgrind or AddressSanitizer (-fsanitize=address).
11. Static vs extern?
static(file scope) — limits a function/variable's visibility to the current translation unit.static(block scope) — keeps a variable across function calls (persists between invocations).extern— declares that a variable is defined in ANOTHER file. Needed for shared globals across.cfiles.
Strings & Arrays
12. Why is strlen(s) O(n)?
C strings are null-terminated; the length is not stored. strlen walks until it finds '\0'. Caching the length yourself is a common optimisation.
13. strcpy vs strncpy vs strlcpy?
strcpy— copies until\0. Buffer-overflow waiting to happen.strncpy— copies at most n bytes, may not null-terminate if input ≥ n.strlcpy(BSD) — copies, guarantees null-termination, returns source length. Safer.
In modern C, prefer snprintf(dst, sizeof dst, "%s", src).
14. Array decays to pointer — what does that mean?
void f(int arr[]) { /* sizeof(arr) is sizeof(int*), not size of array */ }
Passing an array to a function silently converts it to a pointer to its first element. The size info is lost — you must pass it separately.
Structures & Memory Layout
15. What is struct padding?
The compiler may insert hidden bytes between struct members to align each field to its natural boundary. struct { char a; int b; } is usually 8 bytes, not 5.
16. Bit fields?
struct flags { unsigned active : 1; unsigned admin : 1; unsigned color : 4; };
Pack multiple flags into a single byte. Useful in embedded systems and protocol parsers.
17. union vs struct?
A struct stores all members; a union overlays them in the same memory — only one is valid at a time. Used for tagged unions:
struct value { int type; union { int i; double d; char *s; } v; };
Undefined Behaviour
18. Name three undefined behaviours.
- Dereferencing NULL or freed pointer.
- Signed integer overflow.
- Array out-of-bounds access.
- Reading uninitialised memory.
- Modifying the same variable twice in one expression:
i = i++.
19. Why does the compiler optimise dead code away after a UB?
Modern compilers (clang, gcc) assume your code has no UB. If a path implies UB, the compiler may delete it entirely. This is why -fsanitize=undefined is invaluable for catching subtle bugs.
Preprocessor
20. #define vs const?
#define— text substitution by the preprocessor. No type, no scoping.const— typed, scoped, debuggable. Prefer this for constants in modern C.
21. #include <stdio.h> vs #include "myheader.h" ?
< >— search system include directories." "— search the current directory first, then system directories.
