Memory Management
Every `malloc` needs a `free` — track ownership, free once only`free(ptr); ptr = NULL;` — prevents use-after-freeCheck `malloc` return — can return NULL on failure`calloc` zeros memory — `malloc` leaves garbageMemory leaks on early return — ensure cleanup path for all exits
Buffer Overflows
`strcpy` doesn't check bounds — use `strncpy` or `snprintf``gets` is never safe — removed in C11, use `fgets``sprintf` can overflow — use `snprintf` with buffer sizeArray index not checked — manual bounds checking requiredStack buffer overflow corrupts return address — security vulnerability
Strings
Strings need null terminator `\0` — `strncpy` may not add it if truncated`strlen` doesn't count null — allocate `strlen(s) + 1`String literals are immutable — modifying causes undefined behavior`sizeof(str)` vs `strlen(str)` — sizeof includes null, strlen doesn't`char str[]` copies, `char *str` points to literal — different behavior
Pointers
Uninitialized pointer is garbage — always initialize or set to NULLDereferencing NULL is undefined — check before usePointer arithmetic in bytes vs elements — `p + 1` advances by sizeof(*p)Array decays to pointer — `sizeof(arr)` in function gives pointer sizeReturning pointer to local — stack frame gone, dangling pointer
Undefined Behavior
Signed integer overflow is undefined — not wrap-around like unsignedUninitialized variable read — garbage value, unpredictableModifying variable twice between sequence points — `i++ + i++` undefinedNull pointer dereference — crash or worseShift by negative or >= width — undefined
Integer Gotchas
Integer promotion in expressions — small types promote to intUnsigned vs signed comparison — signed converted to unsigned, negative becomes large`sizeof` returns `size_t` (unsigned) — subtraction can wrapDivision truncates toward zero — `-7 / 2` is `-3`, not `-4`
Arrays
Array size must be constant (before C99 VLAs) — use malloc for dynamicVLAs can stack overflow — no size limit check, avoid for large/variable sizes`arr[i]` is `*(arr + i)` — pointer arithmetic under the hoodMultidimensional arrays contiguous — `arr[i][j]` is `arr[i * cols + j]` equivalentCan't return array — return pointer to static or allocated memory
Preprocessor
Macro arguments evaluated multiple times — `MAX(i++, j)` increments i multiple timesWrap macro body in `do { ... } while(0)` — for statement-like macrosWrap arguments in parentheses — `#define SQ(x) ((x) * (x))``#include` order matters — headers may depend on prior includes
Common Mistakes
`=` vs `==` in conditions — `if (x = 5)` assigns, always trueMissing `break` in switch — falls through to next case`sizeof(ptr)` gives pointer size — not array or allocated sizeForgetting to flush stdout — `printf` without `\n` may not appear`fopen` returns NULL on failure — always check before using file pointer