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 variable p. 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 + n adds n * 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.

  • p points to the start of the integer.
  • p + 1 must 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 + 1 0x1000 + (1 * 1) = 0x1001.
  • Stride is tiny. Accesses the very next byte.

Scenario 2: int *p (Size 4 bytes)

  • p + 1 0x1000 + (1 * 4) = 0x1004.
  • Strikes over 4 bytes to the next integer slot.

Scenario 3: struct monster *p (Size 100 bytes)

  • p + 1 0x1000 + (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).

  1. Take address of start (arr).
  2. Jump forward 2 steps (stride of int).
  3. 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++).

  1. Increment p (move pointer to next item).
  2. Return the old value of p.
  3. 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]

  1. Find p.
  2. Look Right: [10] “Array of 10…”
  3. Look Left: * “pointers to…”
  4. Look Left: int “ints.” Result: “Array of 10 pointers to ints.”

Example 2: int (*p)[10]

  1. Find p.
  2. Look Right: ) (Stop).
  3. Look Left: * “Pointer to…”
  4. Jump out of parens. Look Right: [10] “an array of 10…”
  5. Look Left: int “ints.” Result: “Pointer to an array of 10 ints.”

Example 3: void (*signal(int, void (*)(int)))(int);

  1. Find signal.
  2. Look Right: (int, ...) “Function taking int and…”
  3. Look Left: * “returning a pointer to…”
  4. Look Right: (int) “a function taking int…”
  5. Look Left: void “returning void.” Result: “signal is a function that returns a function pointer.”

Practice: Decode the Types

  1. char **argv: Pointer to a pointer to char. (List of strings).
  2. int (*f)(): Pointer to a function returning int.