Pointers and Dynamic Memory: Complete Beginner's Guide
A pointer is a variable that stores a memory address instead of an ordinary value. With the & operator you get a variable's address, and with the * operator you read or write the value at the address a pointer holds. Dynamic memory is memory requested at run time with malloc/calloc, resized with realloc and released with free — unlike arrays, its size can be decided while the program is running.
Pointers are C's superpower: they enable arrays, strings, linked lists and trees, let functions modify caller variables, and avoid copying large data. Dynamic memory matters because input sizes are unknown at compile time — you cannot declare an array for "however many students enrol" until the program runs.
Key Concepts
- Declaration: int *p means "pointer to int"; int x = 10; p = &x stores x's address in p.
- Dereference: *p reads/writes the value at p's address — so *p = 20 changes x itself to 20.
- Pointer arithmetic: for int *p, p + 1 moves forward by sizeof(int) = 4 bytes, landing on the next integer, not the next byte.
- malloc(size): reserves uninitialised bytes; calloc(n, size): reserves and zeroes them; realloc: grows/shrinks a block; free: releases it.
- Memory bugs: leak (never freed — grows until the program dies), dangling pointer (used after free), double free (freeing twice crashes), NULL dereference.
How It Works
How dynamic allocation serves five integers:
- int *p = (int *)malloc(5 * sizeof(int)) asks the OS for 5 × 4 = 20 bytes and returns the block's address into p.
- Always check p != NULL — malloc returns NULL when memory runs out, and dereferencing NULL crashes.
- Use it like an array: p[0] = 10; … p[4] = 50 — the block behaves exactly like int p[5].
- free(p) returns the 20 bytes to the OS; after that, p is dangling and must not be used (set p = NULL for safety).
Worked Example
Predict the output:
int a = 10;<br>int *p = &a;<br>*p = 20;<br>printf("%d %d", a, *p);
- a holds 10; p holds a's address, so *p is an alias for a.
- *p = 20 writes 20 through the pointer — directly into a's memory cell.
- a is now 20, and *p reads that same cell: 20.
Output: 20 20. One variable, two names — that aliasing is why pointers let functions modify caller data.
Common Misconceptions
- "Pointers are just for experts; I can avoid them." In C you cannot — arrays, strings and function parameters all decay to or need pointers; avoiding them means avoiding C itself.
- "malloc'd memory starts at zero." No — malloc leaves garbage; only calloc zeroes the block. Reading uninitialised malloc memory is a classic bug.
- "The OS frees everything, so free() is optional." The OS reclaims memory only when the program exits; a long-running server that never frees will slowly eat all RAM and die.
Real-World Applications
- Data structures: linked lists, trees and graphs are webs of pointers; dynamic allocation grows them node by node.
- Operating systems: kernels manage process memory with pointer arithmetic at massive scale.
- Games and graphics: textures and meshes are malloc'd buffers manipulated through pointers for speed.
- Servers: web servers allocate per-request buffers dynamically since request sizes vary wildly.
A pointer holds an address; & takes one, * follows one, and pointer arithmetic strides by the pointed-to type's size. malloc, calloc, realloc and free give you memory sized at run time — with the strict contract that every allocation is checked for NULL and eventually freed. Master this contract and you master C's deepest and most powerful idea.
Related blogs
- C Programming: Data Types, Operators and Control Statements
- Functions and Recursion in Programming With easy examples.
- Arrays and Strings: Complete Beginner's Guide For students.
- Structures, Unions and File Handling in C For students.
- Python Basics: Syntax, Lists, Dictionaries and Functions
- Object-Oriented Programming: Classes, Inheritance Guide.