The Address Becomes a Value

Since chapter 1 you have written &x and promised yourself an explanation. Here it is: every variable lives at an address, and C lets you store that address in a variable of its own, a pointer. Everything the course has deferred, scanf's magic, the swap that failed, arrays passing without copies, resolves in this chapter, and the machinery is smaller than its reputation: two operators and one discipline.

Declaring, Aiming, Dereferencing

Four moments to slow down for. int *p declares a pointer to int: p's value is an address, and the type records what lives there. p = &score aims it: chapter 1's address-of operator produces the address, and now it is stored. *p, the dereference operator, follows the stored address: reading *p reads score, and the fourth moment is the payoff, writing *p writes score. The variable changed without its own name appearing, which is exactly how scanf has been filling your variables all along.

Printing an address uses %p with a cast to (void *), the anything-pointer type that %p expects; the value itself varies run to run and machine to machine, which is why programs compute with addresses but never hardcode them.

One reading habit prevents most pointer confusion: in declarations, int *p reads inside-out as "*p is an int", and the * binds to the name, so int *a, b; declares one pointer and one plain int, a classic exam trap avoided by declaring pointers one per line.

NULL and the Discipline

NULL is the pointer that deliberately points at nothing, and the course rule mirrors chapter 2's initialize-everything: every pointer starts at NULL or at a real address, never indeterminate. The reason is the usual one with higher stakes: dereferencing an uninitialized pointer is undefined behaviour that writes through a garbage address, and dereferencing NULL is undefined behaviour too, but a NULL pointer can at least be tested:

if (p != NULL) {
    printf("%d\n", *p);
}

An indeterminate pointer cannot be distinguished from a valid one, which is the whole argument for the initialization rule. On this platform AddressSanitizer converts these mistakes into immediate reports; on machines without it they are the crashes and corruptions C is famous for.

Pointers Are Typed

int * and double * are different types, and the compiler enforces the difference: aiming an int * at a double is a constraint violation, not a style issue. The type serves the dereference: *p must know how many bytes to read and how to interpret them, chapter 2's size-plus-interpretation definition of a type, applied through an address. This typing is also what will make pointer arithmetic step by whole elements next lesson.

The Picture to Keep

score:  [    90    ]   at address 0x7ffc...44
p:      [ 0x7ffc...44 ]   the address of score, stored as data

A pointer is a variable whose patch of memory holds the address of another patch. Two boxes, one arrow. Every pointer construct in the coming lessons, arrays, out-parameters, struct pointers, linked nodes, is this picture with more boxes, and when a pointer expression confuses you, drawing the boxes is the reliable way out.

Key Takeaways

  • int *p declares a pointer to int; &x produces an address; *p follows one, and writing *p writes the pointed-to variable.
  • Print addresses with %p and a (void *) cast; never hardcode or assume address values.
  • Every pointer initializes to NULL or a real address; dereferencing NULL or an indeterminate pointer is undefined behaviour, and only NULL is testable.
  • Pointers are typed: the type tells the dereference how many bytes and what interpretation, and mismatched pointer types do not compile.
  • The * binds to the name (int *a, b; makes one pointer); declare pointers one per line.
  • The model is two boxes and an arrow; draw it whenever an expression resists reading.