Functions that hand back more than one answer
return gives one value. Pointer parameters let a function fill in several, which is how scanf has always worked.
After this lesson
You should be able to
- Write a function that returns two results using pointer parameters.
- Explain why scanf needs & but printf does not.
- Determine the scope of a name and predict which variable a line refers to.
Output parameters
A function can only return one value, but many problems have two answers — a quotient and a remainder, a minimum and a maximum. Pass the addresses of the caller's variables and the function can write into them directly.
The convention is worth following: inputs are plain values, outputs are pointers. A reader can then tell from the call alone which arguments the function might change.
#include <stdio.h>
void divide(int a, int b, int *quotient, int *remainder);
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;
}
void divide(int a, int b, int *quotient, int *remainder)
{
*quotient = a / b;
*remainder = a % b;
}This is what scanf has been doing
scanf("%d", &marks) passes the address of marks because scanf must write into your variable. printf("%d", marks) passes the value because printf only reads it. The rule you memorised in week one turns out to be pass by value versus output parameters.
Forgetting the & in scanf passes the value of an uninitialised variable as if it were an address. The program compiles, then writes to a random location — a crash if you are lucky, silent corruption if you are not.
Scope, and passing a pointer onward
A name declared inside a function is visible only inside it, from its declaration to the closing brace. Two functions may both use i without any relationship. A name declared outside all functions is global and visible everywhere, which is why globals cause confusion: any function can change one, so tracking a wrong value means reading the whole program.
A parameter that is already a pointer can be passed straight on to another function without another &. It is already an address, and taking the address of an address gives you something different.
void read_pair(int *a, int *b)
{
scanf("%d", a); /* a is already an address — no & */
scanf("%d", b);
}
void read_and_sum(int *total)
{
int x, y;
read_pair(&x, &y); /* x and y are ints, so & is needed */
*total = x + y;
}Try it yourself
Write a function that takes an array of five ints and reports both the smallest and the largest through output parameters.
Need a hint?
Start both answers at the first element, not at 0 — otherwise an all-negative array reports a maximum of 0.
Check the worked solution
Seeding both from data[0] is the important detail: seeding max at 0 would be wrong for an all-negative array, and seeding min at 0 would be wrong for an all-positive one. Starting from a real element is correct for every input, which is the same reasoning as the accumulator identity values in Unit I.
#include <stdio.h>
void range_of(const int data[], int n, int *smallest, int *largest);
int main(void)
{
int marks[5] = {72, 45, 91, 38, 66};
int low, high;
range_of(marks, 5, &low, &high);
printf("Lowest %d, highest %d\n", low, high); /* 38, 91 */
return 0;
}
void range_of(const int data[], int n, int *smallest, int *largest)
{
*smallest = data[0];
*largest = data[0];
for (int i = 1; i < n; i++) {
if (data[i] < *smallest) *smallest = data[i];
if (data[i] > *largest) *largest = data[i];
}
}Quick check
Why does scanf need &marks while printf does not?
Why this lesson exists
Syllabus mapping
Functions with Output Parameters · Multiple Calls to a Function with Input/ Output Parameters · Scope of Names · Formal Output Parameters as Actual Arguments
Maps to course outcomes CO4, CO5.