C Language Essentials
The language of systems programming. Direct memory access, no garbage collector, compiles to native machine code. Almost every OS kernel, database engine, and embedded system is written in C.
Why It Matters
C is the lingua franca between hardware and software. Understanding C means understanding how computers actually work — memory layout, pointer arithmetic, calling conventions. Even if you primarily write Python or Go, knowing C makes you a better systems thinker.
Compilation Pipeline
source.c → Preprocessor → Compiler → Assembler → Linker → executable
(#include, (C → asm) (asm → .o) (.o → binary)
#define)
gcc -E main.c # stop after preprocessing (expand macros/includes)
gcc -S main.c # stop after compilation (produces main.s assembly)
gcc -c main.c # stop after assembly (produces main.o object file)
gcc main.o utils.o -o program # link object files into executableCore Language Features
Pointers
int x = 42;
int *p = &x; // p holds the ADDRESS of x
*p = 99; // dereference: x is now 99
// Function pointers — callbacks, dispatch tables
int add(int a, int b) { return a + b; }
int (*op)(int, int) = add;
printf("%d\n", op(3, 4)); // 7Structs and Memory Layout
typedef struct {
char name[64]; // offset 0
int age; // offset 64 (possibly padded to 68)
float score; // offset 68
} Student; // total: 72 bytes (with padding)
Student s = {"Alice", 22, 95.5};
printf("%s is %d\n", s.name, s.age);
// Struct padding — compiler aligns fields to their natural boundary
// Use sizeof() to check, never assume sizes
printf("size: %zu\n", sizeof(Student));Manual Memory Management
int *arr = malloc(100 * sizeof(int));
if (!arr) { perror("malloc"); exit(1); }
arr[0] = 42;
free(arr);
arr = NULL; // prevent dangling pointerNo garbage collector. Every malloc needs a free. Forgetting = memory leak. Double-freeing = crash or corruption.
Const and Volatile
const int *p; // pointer to const int — can't modify *p
int *const p; // const pointer to int — can't modify p itself
const int *const p; // both const
volatile int *reg; // tells compiler: don't optimize away reads
// essential for memory-mapped IO and signal handlersUndefined Behavior
C trusts the programmer. Violations don’t always crash — they silently corrupt:
| UB Example | What Can Happen |
|---|---|
| Array out of bounds | Overwrites adjacent memory |
| Signed integer overflow | Compiler may optimize away checks |
| Dereferencing NULL | Segfault (or worse, silent corruption) |
| Using uninitialized variable | Random garbage value |
| Modifying string literal | Segfault (literals are in read-only memory) |
The compiler is allowed to assume UB never happens. This means if (x + 1 < x) (signed overflow check) may be optimized away entirely.
Compilation Flags You Should Always Use
gcc -Wall -Wextra -O2 -g -o program main.c
# -Wall -Wextra: enable warnings (catches most bugs at compile time)
# -fsanitize=address,undefined: runtime detection of memory/UB bugs
# -g: debug symbols for GDBRelated
- Pointers and Memory — deep dive into pointer arithmetic and common bugs
- Memory Allocation — how malloc/free work under the hood
- Makefiles and Build Systems — automating the compilation pipeline
- System Calls — how C programs talk to the kernel