student@ubuntu:~$
pointers-memory Lesson 7 9 min read

Pass-by-Pointer

Fixing the broken swap, output parameters, and why scanf needs &

Reading: Hanly & Koffman: §6.2 (pp. 322–330)

Quick check before you start: Can you explain what &x and *p do? 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 swap function using pointer parameters
  • Implement the output parameter pattern to return multiple values from a function
  • Explain why scanf requires & before its arguments
  • Use const with 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 *a affect the caller’s x.

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 = something changes where the local pointer points (useless – it is a copy). *p = something changes 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 *p means “p is a pointer to an int that is const.” int *const p means “p is a const pointer to an int.”


Check Your Understanding
Why does swap(int a, int b) fail to swap the caller's variables?
AThe function has a logic error in the swap algorithm
BC copies the values into local parameters, so swapping the copies has no effect on the originals
CThe temp variable goes out of scope before the swap completes
DC does not allow functions to modify variables -- only main can do that
Answer: B. C is always pass-by-value. The function receives copies of 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).