Pass-by-Pointer
Fixing the broken swap, output parameters, and why scanf needs &
Quick check before you start: Can you explain what
&xand*pdo? If not, review Pointer Basics first.Practice this topic: Pointers skill drill
After this lesson, you will be able to:
- Explain why C is always pass-by-value, even when passing pointers
- Write a working
swapfunction using pointer parameters - Implement the output parameter pattern to return multiple values from a function
- Explain why
scanfrequires&before its arguments - Use
constwith pointer parameters to indicate read-only access
The Broken Swap
You have seen this before:
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
It swaps the copies but does not affect the caller’s variables. C copies every argument. Always. No exceptions.
The Working Swap
With pointers, we can fix it:
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
Call it:
int x = 5, y = 10;
swap(&x, &y);
printf("x = %d, y = %d\n", x, y); /* x = 10, y = 5 */
Why It Works: Pass-by-Value with Addresses
C is always pass-by-value – even with pointers. When you call swap(&x, &y), C copies the addresses of x and y into parameters a and b. The function receives copies of the pointers, but those copies still point to the original variables.
Before swap(&x, &y):
Caller's variables: Function's parameters:
+---+ +---+
| x | = 5 | a | --> x
+---+ +---+
+---+ +---+
| y | = 10 | b | --> y
+---+ +---+
Inside swap:
*a = *b; /* writes 10 to where a points (x) */
*b = temp; /* writes 5 to where b points (y) */
After swap:
x = 10, y = 5
Key Insight: C is always pass-by-value. When you pass
&x, C copies the address into the parameter. But since both the copy and the original hold the same address, dereferencing either one accesses the same memory location. That is why modifications through*aaffect the caller’sx.
The Output Parameter Pattern
Any time a function needs to “return” more than one value, use pointer parameters:
void divide(int dividend, int divisor, int *quotient, int *remainder)
{
*quotient = dividend / divisor;
*remainder = dividend % divisor;
}
int main(void)
{
int q, r;
divide(17, 5, &q, &r);
printf("17 / 5 = %d remainder %d\n", q, r); /* 3 remainder 2 */
return 0;
}
The caller passes addresses with &, the function writes through *. This is called the output parameter pattern.
Now scanf Makes Sense
Remember scanf("%d", &age)? The & was mysterious earlier. Now it makes sense:
int age;
scanf("%d", &age); /* Pass address so scanf can write to age */
scanf needs to store a value into your variable. Since C is pass-by-value, passing age would give scanf a copy – useless. Passing &age gives it the address, so it can write to the original.
From Java: Java objects are “pass-by-reference-value” – you pass a copy of the reference, but the reference still points to the original object. C’s pass-by-pointer is the explicit version of the same idea: you pass a copy of the address, but the address still points to the original variable. The difference is that C makes this visible and manual.
Common Mistakes
Forgetting & in the caller:
int x = 5;
swap(x, y); /* WRONG: passes values, not addresses */
swap(&x, &y); /* RIGHT: passes addresses */
Forgetting * in the function:
void set_to_zero(int *p)
{
p = 0; /* WRONG: changes the local copy of the pointer */
*p = 0; /* RIGHT: changes the value at the address */
}
Common Pitfall: Inside a function that takes a pointer parameter,
p = somethingchanges where the local pointer points (useless – it is a copy).*p = somethingchanges the value at the address (useful – it modifies the caller’s variable). Always dereference to modify.
const with Pointers
When a function only reads through a pointer (does not modify), mark it const:
void print_value(const int *p)
{
printf("Value: %d\n", *p);
/* *p = 42; Compiler error! Can't modify through const pointer */
}
This tells the caller “I promise not to change your data.”
Two positions, two meanings:
| Declaration | What is constant? | Example |
|---|---|---|
const int *p |
The data pointed to | Cannot write through *p |
int *const p |
The pointer itself | Cannot reassign p |
Reading trick: Read the declaration right-to-left.
const int *pmeans “p is a pointer to an int that is const.”int *const pmeans “p is a const pointer to an int.”
swap(int a, int b) fail to swap the caller's variables?a and b, swaps the copies, and then the copies are destroyed when the function returns. The caller's variables are untouched. The fix is to pass addresses (&x, &y) and dereference inside the function (*a, *b).
What Comes Next
Pass-by-pointer is the mechanism that makes C functions capable of modifying their caller’s data. Output parameters, scanf, swap – they all use the same pattern: pass an address with &, write through it with *.
Next: pointer arithmetic. You will discover that arrays and pointers are deeply connected – arr[i] is syntactic sugar for *(arr + i).