Why this matters
The last lesson covered & and * in isolation – one variable, one pointer, all inside main. This lesson adds
exactly one new idea on top of that: every function parameter in C is a local copy. When you call f(x), the
parameter inside f gets its own box in memory, initialized with a copy of x’s value – changing that box does
nothing to the caller’s x. This is “pass-by-value,” and it’s the only mechanism C has for passing arguments;
there is no pass-by-reference keyword. So how does scanf("%d", &n) manage to fill in your variable, if everything
is passed by value? By having you pass the address of n, not n itself – scanf receives a copy of an
address, and uses the exact *p = trick from the last lesson to reach back into your variable through it. That’s
the whole answer, and it’s the entire subject of this lesson: doing on purpose, across a function call, what you
already did by hand in main last lesson.
Tracing the worked example
try_swap_by_value(x, y) receives copies of x and y in its own parameters a and b. It swaps a and b
perfectly – and then those copies vanish when the function returns. x and y in main were never touched.
swap_by_pointer(&x, &y) instead receives the addresses of x and y. Its parameters a and b are pointers
(int *), copies of the addresses – but an address, unlike a value, still points at the original box. *a = *b
doesn’t touch the pointer a; it reaches through a to overwrite the int that a points at, which is x
itself. This is why swap_by_pointer can do what try_swap_by_value cannot.
The “out-parameter” pattern
A function can only return one value. When you need a function to hand back more than one result – or to modify
something the caller already owns – the standard C idiom is an out-parameter: a pointer parameter the function
writes through instead of returning. You’ll write several of these in this lesson’s exercises. The signature
void divmod(int a, int b, int *quotient, int *remainder) is a direct description of what the function does: take
two values in, write two results out through the pointers.
A pointer is just an address – and an uninitialized one is dangerous
int *p; declares a pointer but does not make it point anywhere valid – it holds garbage until you assign it
(typically with &something, or later, the result of malloc). Dereferencing an uninitialized or NULL pointer
is undefined behavior, not a friendly error message. Every pointer parameter in this lesson’s exercises is
guaranteed to be a valid address by the time your function runs (the caller always passes &x for a real
variable x) – but get in the habit of asking “is this pointer definitely valid?” before you dereference one,
because the language will not ask it for you.