Return Values and How Functions Communicate Results
A function call redirects execution from the caller to the function body, then returns to the caller after the function completes.
The Detour a Function Call Creates
A function call is a detour in the normal path of program execution. The program reaches a call in the caller, redirects execution into the function body, and then returns to the caller after the function completes. Understanding this detour is the foundation for tracing return values and explaining how functions communicate results to the code that called them.
The order in which functions appear in a source file is not necessarily the order in which they run. Execution starts at the main program, or at the first executable statement, and enters a function only when that function is called.
Tracing the Rectangle Calculation
Consider the source-pack scenario of a program that calculates the area of a rectangle by calling a helper function. The important point is not the arithmetic itself. The important point is the movement of execution: the main program reaches the helper-function call, execution moves into the helper function, and execution returns to the main program when the helper finishes.
- Execution begins in the main program rather than inside the helper function merely because the helper appears earlier in the file.
- The main program reaches the statement that calls the rectangle-area helper function.
- The call redirects execution into the helper function's body.
- The helper function completes its work.
- Execution returns to the caller, allowing the main program to continue with the statement after the call.
A function's position in the file does not make it execute immediately. A call determines when execution enters the function, and completion determines when execution returns to the caller.
How the Call Stack Finds the Return Point
The program needs to remember where it was in the caller before it enters a function. The call stack provides that memory. Each time a function is called, the program stores information about the current location, called the return address, on the call stack. When the function completes, the program removes that return address from the stack and uses it to jump back to the correct place in the caller.
The same mechanism also works when functions call other functions. Each new call pushes another return address onto the call stack. Each completed call pops one return address off. This lets the program return through nested calls in the correct order rather than losing track of earlier callers.
Following Results Back to the Caller
A function is useful to its caller because the caller can rely on the function to complete a separate piece of work and then return control. In a calculation such as the rectangle-area scenario, the work performed inside the helper produces the calculation's result. Once the helper completes, execution is back in the caller, where the program can continue using the outcome of that call.
When discussing a return value, trace two connected paths: the control path back to the caller and the result path from the completed function to the code that called it.
Predicting the Next Execution Point
What do you think happens?
A program is executing in its main section and reaches a call to a helper function. What executes next, and where does execution go after the helper completes?
Reveal answer
Answer: The first statement in the called function executes next, and the caller resumes after the call when the function completes.
A function call redirects execution into the called function body. The call stack preserves the caller's return address, so completion can transfer execution back to the correct location. Source-file order does not determine this path.
Describe the execution path for the rectangle-area scenario in five stages: starting location, call location, function location, completion, and resumption location.
Hints
- Begin in the main program, not at the helper's position in the file.
- Identify the statement that calls the helper.
- Name the function body as the detour.
- Use the return address to identify where the caller resumes.
When debugging an unexpected result, trace execution one call at a time. Mark where the caller pauses, which function begins executing, what work that function completes, and where the caller resumes. This prevents the source file's top-to-bottom appearance from misleading you about the actual execution order.
Mistakes in Execution Tracing
Assuming functions execute in the order in which their definitions appear in the file.
Execution starts at the main program or first executable statement and enters a function when that function is called.
Fix:
Find the starting executable statement, then follow each function call as a redirection of execution.Assuming execution remains in the caller while the called function performs its work.
A function call redirects execution into the function body before the caller continues.
Fix:
Pause the caller at the call, trace the function body, and continue only after the function completes.Ignoring the return address when tracing nested calls.
Each call stores a return address on the call stack, and each return uses one saved address.
Fix:
Track each call as a push of a return address and each completion as a pop of one return address.
Key Takeaways
- A function call redirects execution from the caller into the function body.
- After the function completes, execution returns to the caller.
- The call stack stores return addresses so the program knows where to resume.
- Nested calls work because each call pushes a return address and each return pops one.
- Tracing calls and returns helps explain results and debug unexpected program behavior.
Key Takeaways
- A function call creates a temporary detour from the caller into a function body.
- The caller resumes after the function completes.
- The call stack preserves return addresses and supports nested function calls.
- Execution order follows calls, not simply the order of function definitions in the source file.
- Tracing both control flow and the resulting value helps explain how functions communicate results.