Parameters and return values
Understand how C passes arguments by value, how to use pointers to modify caller variables, and patterns for returning multiple results.
- Explain that C passes arguments by value (copy)
- Demonstrate that modifying a parameter does not change the caller's variable
- Use a pointer parameter to allow a function to modify the caller's variable
- Return a computed value and distinguish that from an output parameter
When you call a function in C, arguments are copied into the function's local variables. This "pass by value" behaviour is straightforward but trips up many beginners who expect functions to modify their arguments. Understanding this rule — and knowing how to work around it with pointers — is essential before moving on.
Pass by value
#include <stdio.h>
void double_it(int x) {
x = x * 2;
printf("Inside double_it: x = %d\n", x);
}
int main(void) {
int n = 5;
double_it(n);
printf("After call: n = %d\n", n); /* still 5 */
return 0;
}Output:
Inside double_it: x = 10
After call: n = 5n is unchanged. The function received a copy of n called x. Modifying x has no effect on n. This is pass by value.
Returning a value instead
The clean solution is to compute a new value and return it:
int doubled(int x) {
return x * 2;
}
int main(void) {
int n = 5;
n = doubled(n);
printf("n = %d\n", n); /* 10 */
return 0;
}Prefer this pattern whenever the function produces a single result. Return the value; let the caller decide what to do with it.
Output parameters with pointers
When a function needs to produce more than one result, or genuinely needs to modify the caller's variable, you pass a pointer (an address) instead of a value. The function dereferences the pointer to reach the original variable.
This is a preview of the Pointers module — for now, focus on recognising the pattern:
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(void) {
int x = 10, y = 20;
printf("Before: x=%d y=%d\n", x, y);
swap(&x, &y); /* pass addresses, not values */
printf("After: x=%d y=%d\n", x, y);
return 0;
}Output:
Before: x=10 y=20
After: x=20 y=10&x means "the address of x." Inside swap, *a means "the value at address a." The Intermediate tier (Pointers module) covers this in depth. For now, just recognise that this is how you write functions that modify their arguments.
Returning structs (preview)
Once you learn structs (Intermediate tier), you can return multiple values as a single compound type:
typedef struct { int quotient; int remainder; } DivResult;
DivResult divide(int a, int b) {
DivResult r;
r.quotient = a / b;
r.remainder = a % b;
return r;
}This is cleaner than output parameters for many use cases. For now, return a single value or use output parameters.
The return statement
A function returns as soon as it hits a return statement. Multiple returns are valid:
int absolute_value(int n) {
if (n < 0) {
return -n; /* early return */
}
return n;
}Both returns are correct. Some style guides prefer a single return at the end for clarity; others embrace early returns as cleaner for guard clauses. The C standard library uses both styles.
All code paths must return a value. If a non-void function can reach the end without a return, the compiler (with -Wall) warns about "control reaches end of non-void function." gcc may let you run it, but the return value is garbage. Treat this warning as an error.
Functions calling functions
Functions compose freely:
#include <stdio.h>
int clamp(int value, int min, int max) {
if (value < min) { return min; }
if (value > max) { return max; }
return value;
}
void print_bar(int value, int max) {
int width = clamp(value, 0, max);
for (int i = 0; i < width; i++) {
printf("#");
}
printf("\n");
}
int main(void) {
print_bar(7, 20); /* ####### */
print_bar(20, 20); /* #################### */
print_bar(-3, 20); /* (empty) */
return 0;
}Breaking complex problems into small, single-purpose functions makes code easier to read and test.
Where to go next
Next: scope and lifetime — where variables exist, how long they last, and why local variables in functions are invisible from the outside.
Declaring and defining functions
Learn how to declare and define functions in C — including the declaration vs definition distinction, function prototypes, and void functions.
Scope and lifetime
Understand where variables are visible in C (scope) and how long they exist in memory (lifetime) — including local, global, and static local variables.