Unit 2 · Lesson 618 minAcademic review pending

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.

Choose explanation

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.
01

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.

Two inputs by value, two outputs by address
#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;
}
02

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.

03

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.

& is needed for a variable, never for a pointer already in hand
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?

Select an answer to check your thinking.

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.