New C programmers often treat pointers as black magic. They are not. They are simply numbers that correspond to house addresses on a very long street.
The Mental Model: The Postman
Imagine memory as a single street of one billion mailboxes (bytes).
- Each mailbox has an Address (0 to 1,000,000,000).
- Each mailbox holds a Value (a single byte, 0-255).
A Pointer is just a piece of paper with a mailbox number written on it.
int x = 42;: “Put the value 42 in mailbox #500.”int *p = &x;: “Create a new variablep. Inside it, write the number 500.”
Dereferencing (*p)
*p means: “Go to the mailbox number written on p. Read strictly what is inside.”
If p says 500, *p is the value inside mailbox #500 (which is 42).
The “Golden Rule” of Pointer Arithmetic
The single most confusing concept is that p + 1 does not simply add 1 to the address number.
The STRIDE Rule
p + naddsn * sizeof(TYPE)to the raw address.
Why?
If p is an int * (simulating a 4-byte integer), it represents a logical “block” of 4 bytes.
ppoints to the start of the integer.p + 1must point to the next integer, not the middle of the current one.
Visualizing the Stride
Let p be at address 0x1000.
Scenario 1: char *p (Size 1 byte)
p + 10x1000 + (1 * 1) = 0x1001.- Stride is tiny. Accesses the very next byte.
Scenario 2: int *p (Size 4 bytes)
p + 10x1000 + (1 * 4) = 0x1004.- Strikes over 4 bytes to the next integer slot.
Scenario 3: struct monster *p (Size 100 bytes)
p + 10x1000 + (1 * 100) = 0x1064.- Jumps an entire structure length.
This explains why types matter. A void * has no size, so void * arithmetic is technically illegal in standard C (though standard GNU C treats it as size 1).
Array Decay: The Great Illusion
In C, int arr[5] and int *p are intimately related but validly different.
The Illusion: When you write arr in code, the compiler silently translates it to “pointer to the first element’s address” (&arr[0]).
This is why arr[2] works. It translates to *(arr + 2).
- Take address of start (
arr). - Jump forward 2 steps (stride of
int). - Look inside (
*).
The Double-Pointer (int **)
Commonly used for 2D arrays (like argv).
Think of it as “A list of lists”.
argv: The Master List. Finds the address of a sub-list.argv[0]: A specific sub-list (a string). Finds the address of a character.argv[0][1]: The character itself.
Layout of char *argv[]:
argv (Main Array) Strings (Scattered in Memory)
[ Ptr0 ] ------------> "program_name\0"
[ Ptr1 ] ------------> "-v\0"
[ Ptr2 ] ------------> "input.txt\0"
[ NULL ]Common Pitfalls (The “Gotchas”)
1. The Priority Trap
*p++
Does this increment the value inside p? Or the address p?
Answer: It parses as *(p++).
- Increment
p(move pointer to next item). - Return the old value of
p. - Dereference that old value. Result: You get the current item, and the pointer advances. Classic iteration idiom.
2. The Uninitialized Arrow
int *p;
*p = 5;CRASH. You created a piece of paper (p) but didn’t write a mailbox number on it. It contains garbage (random number). You just shoved the value 5 into a random mailbox, possibly overwriting your operating system or crashing via Segfault.
Always initialize pointers: int *p = &x or int *p = malloc(...).
The “Right-Left” Rule
How do you read specific C abominations like int *(*fp)(int *)?
Use the Right-Left Rule:
Start at the variable name. Read Right until you hit a
)or the end. Then read Left until you hit a(or the start. Repeat.
Example 1: int *p[10]
- Find
p. - Look Right:
[10]“Array of 10…” - Look Left:
*“pointers to…” - Look Left:
int“ints.” Result: “Array of 10 pointers to ints.”
Example 2: int (*p)[10]
- Find
p. - Look Right:
)(Stop). - Look Left:
*“Pointer to…” - Jump out of parens. Look Right:
[10]“an array of 10…” - Look Left:
int“ints.” Result: “Pointer to an array of 10 ints.”
Example 3: void (*signal(int, void (*)(int)))(int);
- Find
signal. - Look Right:
(int, ...)“Function taking int and…” - Look Left:
*“returning a pointer to…” - Look Right:
(int)“a function taking int…” - Look Left:
void“returning void.” Result: “signal is a function that returns a function pointer.”
Practice: Decode the Types
char **argv: Pointer to a pointer to char. (List of strings).int (*f)(): Pointer to a function returning int.