Lessons
Structs: typedef, member access, and padding
struct definition, typedef, dot (.) vs arrow (->) operator, struct assignment (shallow copy), and struct padding/alignment.
What to look for:
- Use typedef to avoid writing struct everywhere: typedef struct { ... } MyType;
- dot operator for stack struct: acc.balance; arrow for pointer: p->balance
- Struct assignment copies all bytes — including pointer fields (shallow copy)
- Field order matters for padding: put largest fields first to minimise wasted bytes
Heap memory: malloc, calloc, realloc, free
Dynamic allocation, checking for NULL return, realloc pitfalls, free patterns, and common errors (leak, double-free, use-after-free).
Prerequisite: Foundations Track
What to look for:
- Always check: if (p == NULL) { handle error; } before using a malloc result
- realloc may return a different pointer — never: arr = realloc(arr, ...) (leak on failure)
- Correct realloc: int *tmp = realloc(arr, new_size); if (tmp) arr = tmp;
- After free(p): set p = NULL to prevent accidental use of dangling pointer
Strings: null termination, strcpy, snprintf
char arrays as strings, null terminator, strlen, strncpy vs strcpy, snprintf for safe formatting, and strtol for parsing.
Prerequisite: Lesson 2
What to look for:
- C strings are null-terminated char arrays — the '\0' must always be there
- strcpy is unsafe (no bounds check) — use strncpy or strlcpy
- snprintf is the safe way to format strings: snprintf(buf, sizeof(buf), "...")
- strlen is O(n) — it walks to the null terminator; don't call it in a hot loop
The preprocessor: #define, #include, and macros
#include, #define (constants and macros), #ifdef/#ifndef, header guards, and why macros are dangerous without parentheses.
Prerequisite: Lesson 3
What to look for:
- Header guards: #ifndef MY_HEADER_H / #define MY_HEADER_H / ... / #endif
- Macro pitfall: #define SQUARE(x) x*x — SQUARE(1+2) expands to 1+2*1+2 = 5, not 9
- Safe macro: #define SQUARE(x) ((x)*(x)) — parenthesise arguments and the whole expression
- Prefer const int and inline functions over macros where possible (C99+)
File I/O: fopen, fprintf, fgets, fclose
FILE*, fopen modes (r/w/a), fprintf, fputs, fgets, fread/fwrite, fclose, error handling with ferror, and the importance of always closing files.
Prerequisite: Lessons 1–4
What to look for:
- fopen returns NULL on failure — always check before using the FILE*
- fgets reads a line but includes the newline — check and strip with strcspn
- fread/fwrite for binary data — use sizeof to get correct byte counts
- fclose on all paths — even error paths; a leaked FILE* means buffered data may not be flushed
Essential practice: Run all malloc/free code with -fsanitize=address — it will catch leaks and invalid accesses immediately. valgrind --leak-check=full is also invaluable for heap debugging.
Track quiz
4 questions to check your understanding.
What is wrong with int *p = realloc(p, new_size)?
Why is strcpy unsafe?
Why does #define SQUARE(x) x*x give wrong results for SQUARE(1+2)?
What does fopen return if the file cannot be opened?