The Back Door, Opened

Chapter 9 ended with a promise: pass-by-value is the rule, and addresses are the back door with its own chapter. This is that chapter, and the door opens on the exact function that failed there. A function that receives addresses can modify the caller's variables through them, pass-by-value never violated, because what is copied is the address, and a copied address still reaches the original.

swap, Finally

x = 2, y = 1: the output chapter 9 could not produce. Compare the two versions character by character, because exams do. The parameters became int *; the call site passes &x, &y; and every touch inside became a dereference: *a, *b. The three-assignment temp dance is unchanged, but it now moves values through addresses into the caller's variables. The failed version swapped copies; this one swaps the originals, and the one-line explanation reads: the function holds the callers' addresses, so its writes land at home.

Out-Parameters: Returning More Than One Value

return carries one value; pointers carry as many as you pass. A parameter used to deliver a result through its address is an out-parameter, and the classic demonstration answers two questions at once:

The inputs travel by value, the two answers travel by address, and the signature documents which is which: plain parameters in, pointer parameters out. This is scanf's design exactly, its format string plus &-ed variables are out-parameters filled by parsing, and the symmetry you have used since chapter 1 is now something you can build.

The professional refinement worth naming: pointer parameters that only read (a large struct passed by address for cheapness, next lesson's business) differ from out-parameters in intent, and real APIs mark the read-only ones const. Course code keeps signatures small enough to read at a glance; the convention arrives fully in the capstone.

The Discipline at the Boundary

A function trusting its pointer parameters inherits the caller's mistakes, so the guards move with the addresses. The course rule: a function that dereferences a pointer parameter documents (or checks) that it must not be NULL. For the small helpers of this chapter, the contract is documentation, swap's callers pass addresses of real variables, and that is that; when NULL is a meaningful input (optional results, missing records), the function tests it explicitly. What must never happen is a shrug: every pointer parameter is either guaranteed non-NULL by contract or checked in code.

Returning pointers has one absolute prohibition, worth learning before dynamic memory makes returning pointers routine: never return the address of a local variable. The local dies at return, chapter 9's lifetime rule, and the returned address dangles at dead stack memory; using it is undefined behaviour. gcc warns; the warning is always right.

Reading Pointer Signatures

Fluency test, because exams print these cold:

  • void f(int n): input by value; caller unaffected.
  • void f(int *n): address received; f can read and write the caller's int, out-parameter or in-out.
  • void f(int *arr, int count): last lesson's array convention, a run of ints plus its length.

The declaration syntax cannot distinguish "pointer to one int" from "pointer to the first of many": int *n and int arr[] are the same type at the boundary. The name, the count parameter, and the documentation carry that meaning, which is why C code leans hard on naming conventions at function boundaries.

Key Takeaways

  • Passing &x lets a function write the caller's variable through *param: swap works, with the copied-address explanation ready for exams.
  • Out-parameters deliver multiple results: values in, addresses out, scanf's own design now in your hands.
  • Every pointer parameter is non-NULL by documented contract or checked in code; no shrugs.
  • Never return a local's address: the stack frame dies at return and the pointer dangles, UB on use.
  • int *n at a boundary may mean one int or an array's start; names and count parameters carry the difference.