Pointers and Memory
A pointer is a variable that holds a memory address. Pointer arithmetic, combined with C’s manual memory model, gives you direct control over how data is stored and accessed — and direct responsibility for getting it right.
Why It Matters
Every segfault, buffer overflow, and use-after-free bug traces back to pointer misuse. Understanding pointers means understanding how data lives in memory — essential for systems programming, embedded development, and debugging anything written in C.
Stack vs Heap
High addresses
┌──────────────────┐
│ Stack │ ← local variables, grows DOWN
│ ↓ │
│ │
│ ↑ │
│ Heap │ ← malloc'd memory, grows UP
├──────────────────┤
│ BSS (zeros) │ ← uninitialized globals
├──────────────────┤
│ Data (init'd) │ ← initialized globals
├──────────────────┤
│ Text │ ← compiled code (read-only)
└──────────────────┘
Low addresses
Stack allocation is automatic and fast (just move the stack pointer). Heap allocation (malloc) is flexible but slower and requires manual free.
Pointer Arithmetic
int arr[] = {10, 20, 30, 40};
int *p = arr; // points to arr[0]
p++; // moves sizeof(int) bytes forward → arr[1]
printf("%d\n", *p); // 20
// Array indexing IS pointer arithmetic
arr[2] == *(arr + 2) // both give 30
// Pointer difference gives element count, not byte count
int *end = arr + 4;
printf("%td elements\n", end - arr); // 4Pointer Variants
void *vp; // generic pointer — must cast before dereference
int **pp; // pointer to pointer — used for dynamic 2D arrays
const int *p; // can't modify the pointed-to value through p
int *const p = &x; // can't change where p pointsDouble Pointers
// Common use: function that allocates memory for the caller
void alloc_buffer(char **buf, size_t size) {
*buf = malloc(size);
}
char *data;
alloc_buffer(&data, 1024); // data now points to heap memory
free(data);Common Bugs
Dangling Pointer
int *p = malloc(sizeof(int));
*p = 42;
free(p);
// p still holds the old address — DANGLING
*p = 99; // UNDEFINED BEHAVIOR — may crash, may silently corrupt
p = NULL; // fix: always null after freeDouble Free
free(p);
free(p); // heap corruption — exploitable security vulnerabilityBuffer Overflow
char buf[8];
strcpy(buf, "this string is way too long"); // overwrites past buf!
// Fix: strncpy(buf, src, sizeof(buf) - 1); buf[sizeof(buf)-1] = '\0';Use of Uninitialized Pointer
int *p; // garbage address
*p = 42; // writing to random memory — UBDebugging with Valgrind
gcc -g -O0 -o program program.c
valgrind --leak-check=full ./programTypical Valgrind output for a leak:
==12345== 40 bytes in 1 blocks are definitely lost
==12345== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/...)
==12345== by 0x401145: main (program.c:8)
| Valgrind Error | Meaning |
|---|---|
| Invalid read/write | Accessing freed or out-of-bounds memory |
| Definitely lost | Memory leaked — no pointer to it remains |
| Conditional jump on uninitialized | Using a variable before setting it |
Compile with -fsanitize=address (ASan) as an alternative — faster than Valgrind, catches most of the same bugs at runtime.
Related
- C Language Essentials — language basics and compilation
- Memory Allocation — how malloc/free work under the hood
- Debugging with GDB — stepping through pointer bugs
- Memory Management — OS-level virtual memory and page tables