A function call depends on more than a name. Scope determines which declarations that name can refer to. Default arguments supply omitted inputs. Overloading lets several functions share a name when their parameter lists differ. Each feature answers a different question, so it helps to examine them one at a time.
Start with CS1: functions and references if the difference between a declaration and a call is still new. This lecture explains how the compiler connects a call to the appropriate function.
Which variable does a name mean? #
A local variable belongs to the block where it is declared. A nested pair of braces introduces an inner block. Suppose an outer count contains three, and an inner block declares another count containing ten. Inside the inner block, the name count refers to the inner variable. Assigning twelve there changes the inner variable, while the outer one still contains three.
When the inner block ends, its local variable is no longer available. The outer declaration becomes visible again, so using count afterward refers to the outer object. This is hiding, not replacement. Keeping two separate boxes in a trace prevents the mistaken conclusion that the inner declaration overwrote the outer value.
Two different functions can also declare variables with the same name without sharing them. A name by itself does not establish a connection between objects. Shared access must come from a suitable parameter, reference, pointer, or a declaration available to both functions.
A mutable global variable can be accessed from many places. That makes a changing value harder to trace, because several functions may have written it. Passing needed values as arguments makes those dependencies visible at the call. A shared constant can express a fixed quantity without allowing all those functions to change it.
What happens when an argument is omitted? #
A default argument lets a caller leave out a trailing argument. Consider this declaration:
int sum_range(int first, int last = 5);The function still has two integer parameters. The default does not create a second implementation or a second one-parameter function. Instead, it supplies the missing input at a call.
For sum_range(2), first receives two and last receives five. For sum_range(2, 8), first receives two and last receives eight. An explicitly supplied argument takes precedence over the default. The declaration does not show what sum_range computes; its body or contract must explain that separately.
Defaults work from the end of the ordinary argument list. After a parameter has a default, subsequent ordinary parameters must also have defaults available. Otherwise, omitting an argument would leave the compiler without a clear way to match the remaining inputs to the parameter positions.[1]
When tracing a call, write down the complete effective argument list before entering the body. This turns an omitted argument into an ordinary supplied value and avoids wondering whether a later calculation somehow guesses it.
Which function is called when names match? #
Overloaded functions share a name but differ in parameter types or numbers. The compiler examines the arguments and chooses the suitable overload. Renaming a parameter does not create an overload, because callers supply values, not the parameter's internal name. Changing only the return type is also insufficient.
int twice(int value) { return value * 2; }
double twice(double value) { return value * 2.0; }With an integer argument, the integer version is the appropriate exact match. Inside that version, value is an integer, and the multiplication uses the integer literal two. With a double argument, the double version is the exact match and performs the corresponding floating-point calculation.
A trace should begin by identifying the chosen version. Reading the body of the wrong overload can make correct code seem inconsistent. The expected destination of the result does not turn two functions differing only by return type into valid overloads.
Conversions and defaults can make less simple calls ambiguous: more than one declaration may appear usable without one being the clear best choice. Resolve that through a clearer interface or a deliberately typed argument, rather than assuming the compiler picks whichever body looks convenient. A shared name is most useful when the functions perform closely related tasks.
Returning and changing data are still separate #
A return ends the current function call. In a void function, return; exits without a result value. A reference parameter can modify the caller before the function returns, but the reference is not itself a return value.
Use this order for a careful trace: find the declarations visible at the call, identify the overload, fill in defaults, establish each local parameter, follow any changes, and finally record the returned result. Afterward, resume the caller at the point where it was waiting.
Practice and explanation #
An inner block declares its own count and changes it. Does that change an outer variable with the same name? No. The two declarations create separate objects; the inner name temporarily hides the outer name.
Can you create two versions of twice by changing only the parameter name from value to number? No. The parameter types and count remain the same, so the interface has not become a distinct overload. Can a supplied second argument override a default? Yes: the default is used only when that argument is omitted.