CReturn Values

Return Values

The return statement is how a function sends a result back to whoever called it. It also immediately ends the function — no statement after return in the same call will execute.

The return Statement

Returning a value

C
int square(int n) {
    return n * n; // computes n * n and sends it back, then exits
}

The type of the expression after return should match the function's declared return type (or be convertible to it). The value produced by the call expression, e.g. square(4), is the value passed to return.

A Function Can Only Return One Value

Unlike some languages that let you write return a, b; to hand back multiple values at once, C's return statement carries exactly one value.

Simulating multiple return values
When you need to hand back more than one piece of information, C programmers use one of a few patterns: pass extra pointers as *output parameters* and write results through them, bundle the values into a `struct` and return that struct by value, or use a global/static variable (rarely recommended). We will cover output parameters in depth on the pass-by-reference page, and structs in their own section.
void Functions

A function declared to return void produces no value at all. It can still use a bare return; to exit early, or it can simply let control reach the closing } of its body, which ends the function just as effectively.

Early return in a void function

C
#include <stdio.h>

void printPositive(int n) {
    if (n <= 0) {
        return; // bare return: exit early, nothing to print
    }
    printf("%d is positive\n", n);
    // falling off the end here also returns, implicitly
}
Guard Clauses: Returning Early

A very common and readable pattern is to check for invalid or trivial input at the top of a function and return immediately, rather than nesting the "real" logic inside a big if block.

Guard clause style

C
#include <stdio.h>

double safeDivide(double numerator, double denominator, int *ok) {
    if (denominator == 0.0) {
        *ok = 0;       // signal failure through an output parameter
        return 0.0;    // return early; skip the real computation
    }

    *ok = 1;
    return numerator / denominator;
}

int main(void) {
    int success;
    double result = safeDivide(10.0, 0.0, &success);

    if (!success) {
        printf("Cannot divide by zero\n");
    } else {
        printf("Result: %.2f\n", result);
    }
    return 0;
}
Every Path Must Return (for non-void functions)

If a function's return type is not void, every possible path through the function should end with a return that produces a value. Forgetting one is a mistake compilers usually warn about, but C will still let you compile it.

A function that forgets to return on one path

C
int sign(int n) {
    if (n > 0) {
        return 1;
    } else if (n < 0) {
        return -1;
    }
    // BUG: no return here for the n == 0 case!
}
Missing return value is undefined behavior
If control reaches the end of a non-void function without executing a `return` with a value, and the caller then *uses* the function's result, the behavior is undefined — the "returned" value could be leftover garbage from a CPU register or the stack. Some compilers warn ("control reaches end of non-void function"), but they are not required to, and the program may appear to work by accident on one machine and misbehave on another. Always make sure every code path returns an appropriate value.
  • return expr; — exits the function immediately and yields expr to the caller.

  • return; — valid only in void functions; exits with no value.

  • Falling off the closing } of a void function is equivalent to a bare return;.

  • Falling off the closing } of a non-void function is undefined behavior if the result is used.

Up next
Now that you understand return values, we'll look closely at how arguments are passed into a function — starting with pass-by-value and the classic broken-swap example.