Pointers & Functions
Call by referenceA function only ever gets copies — but if the copy is an address, the function can reach back into the caller's memory. In the lab below you watch a swap that finally works, a function that hands back two results at once, and the classic bug of returning the address of a variable that no longer exists.
- f(&x)sends where x lives
- *p = …changes the caller's variable
- return &localis a dangling pointer
Ready
Keyboard: ← → step · Space play / pause · R reset · Esc exit classroom mode
The ideas behind the lab
Exactly what you watched happen, in plain steps.
Passing an address
C always passes arguments by value: the function gets copies. To let a function change a variable of the caller, pass the variable's address (&x) and declare the parameter as a pointer (int *p). The function then writes through the pointer (*p = …) straight into the caller's memory. This is how C does "call by reference" — and why scanf needs &.
void twice(int *p) // p receives an address
{
*p = *p * 2; // changes the caller's variable
}
int x = 5;
twice(&x); // send the address of x
printf("%d", x); // 10
| void f(int n) | void f(int *p) | |
|---|---|---|
| Call | f(x) | f(&x) |
| The function gets | a copy of the value | a copy of the address |
| Can it change x? | No | Yes: *p = … |
| Typical use | inputs | outputs, swap, big data |
| Example | square(x) | scanf("%d", &x) |
Call by reference
- The caller writes &x to send where x is.
- The parameter is a pointer: void f(int *p).
- Inside, *p is the caller's x: read it or assign to it.
- Changing p itself (p = …) does not affect the caller — only *p does.
- Arrays are always passed this way (Module 26): a[i] in the function is the caller's element.
Several results
- return gives back only one value.
- For more results, pass the address of a variable for each one.
- The function fills them: *quotient = a / b; *remainder = a % b;
- These are called output parameters.
- Many library functions work this way, e.g. scanf fills the variables you pass.
Rules for safe pointers
- Never return the address of an ordinary local variable — it dies with the frame.
- Returning the address of a static, a global or malloc'd memory is fine.
- Use const int *p for a parameter the function must only read.
- Check pointer parameters for NULL when a caller might pass NULL.
- Watch the precedence: (*p)++ increments the value; *p++ moves the pointer.
Minimum and maximum in one call, and a running total
Every line, explained.
#include <stdio.h>
void minMax(const int a[], int n, int *min, int *max)
Two output parameters.
{
*min = *max = a[0];
Start both at the first element.
for (int i = 1; i < n; i++) {
if (a[i] < *min) *min = a[i];
if (a[i] > *max) *max = a[i];
}
}
void addTo(int *total, int amount)
{
*total += amount;
Changes the caller's variable.
}
int main(void)
{
int v[] = {7, 2, 9, 4};
int lo, hi;
Filled by minMax.
minMax(v, 4, &lo, &hi);
printf("min %d, max %d\n", lo, hi);
Prints min 2, max 9
int sum = 0;
addTo(&sum, 5);
addTo(&sum, 10);
printf("sum %d\n", sum);
Prints sum 15
return 0;
}
Spot the bug
Mistakes almost every beginner makes — and how to fix them.
Forgetting the & in the call
swap(x, y); // sends values
swap(&x, &y);
The function expects addresses. With values, the compiler warns and the program writes to random memory.
Changing the pointer instead of the value
void reset(int *p) { p = 0; } // p = NULL
void reset(int *p) { *p = 0; } // the int becomes 0
Without * you only change the local copy of the address.
*p++ instead of (*p)++
void inc(int *p) { *p++; } // moves p
void inc(int *p) { (*p)++; }
++ binds tighter than *: *p++ means *(p++). The brackets make it increment the value.
Returning the address of a local
int *make(void) { int n = 5; return &n; }
int make(void) { return 5; } // or malloc / static
n disappears when make returns; the caller gets a dangling pointer.
Using an output parameter before it is set
int q;
printf("%d", q); // garbage
divide(17, 5, &q, &r);
int q, r;
divide(17, 5, &q, &r);
printf("%d", q);
q gets its value inside divide — read it only after the call.
Check your understanding
Answer each question — you will see the explanation straight away.
void f(int *p) { *p = 7; } — int x = 1; f(&x); What is x?
*p is x, so x becomes 7.
Why does scanf("%d", &n) need the &?
scanf fills your variable through its address.
void g(int *p) { (*p)++; } — int x = 4; g(&x); g(&x); What is x?
Each call adds 1 to x.
Which is the safe one to return from a function?
A static variable lives for the whole run.
How can one function give back a quotient AND a remainder?
Output parameters: the caller passes &q and &r.
Pointers & Functions quiz
Test yourself: 10 questions, 70% to pass. Create a free account to take the quiz, save your progress and work towards the certificate.