Prototypes, extern linkage, default values, overloading, function stacks, inline, name mangling, and overload resolution.
Functions must be declared before they are called. The compiler needs to know a function's signature — its name, return type, and parameter types — before it can validate calls to it.
A prototype is a declaration without a body, placed above the caller (typically at the top of the file or in a header):
// Prototype (declaration only)
double Area(double radius);
int main() {
Area(5.0); // valid — compiler already knows Area's signature
}
// Full definition can come after
double Area(double radius) {
return 3.14159 * radius * radius;
}
.h) work — they declare functions so other translation units can call them, with the actual definition living in a separate .cpp file that is resolved at link time.extern Keywordextern specifies that a symbol has external linkage — it can be accessed from a different translation unit. Can be applied to: Global Variables, Functions, Template Declarations.
extern makes them accessible across translation units.staticThe counterpart to extern is using the static keyword at file scope. It gives a symbol internal linkage — the opposite of extern — meaning it's only visible within its own translation unit and can't be accessed from other files.
// fileA.cpp
static int counter = 0; // only accessible within fileA.cpp
// fileB.cpp
extern int counter; // linker error — counter has internal linkage
static inside a function body means something different (persistent local variable). This is specifically about static at the top level of a file.A non-const global variable is extern by default. All other declarations of it across files must use extern to tell the linker which definition is the real one:
// fileA.cpp
int i = 42; // declaration AND definition
// fileB.cpp
extern int i; // declaration only — same as i in fileA
// fileD.cpp — ERROR
int i = 43; // LNK2005: 'i' already has a definition
extern int i = 43; // same error (extern ignored on definitions)
const is internal by default. To make a global const accessible externally, add extern to both the initial definition and all declarations.
Parameters can have default values, making them optional at the call site. Defaults must be placed at the end of the parameter list — you cannot have a required parameter after a defaulted one.
double Area(double radius, double pi = 3.14159);
Area(5.0); // uses default pi
Area(5.0, 3.14159265); // overrides default pi
Arrays decay to a pointer when passed to a function. Both syntaxes below are equivalent — the function receives a pointer, not a copy:
void Print(int numbers[]); // array syntax
void Print(int* numbers); // pointer syntax — identical
std::array / std::vector instead.A reference parameter binds directly to the caller's variable — no copy is made and changes affect the original:
void Double(int& x) {
x *= 2;
}
int val = 5;
Double(val); // val is now 10
const & to pass efficiently without allowing modificationWhen a function is called, the CPU uses a call stack to track where to return to. The sequence is:
The inline keyword is a request to the compiler to paste the function's code directly at the call site instead of generating a CALL instruction. This eliminates the function-call overhead.
inline int Square(int x) {
return x * x;
}
inline if the function is too complex or inlining would increase code size unacceptably. Modern compilers also inline functions without the keyword when they judge it beneficial.
Functions with the same name but different parameter lists are called overloaded functions. The compiler picks the right version based on the arguments at the call site.
double Area(double radius); // circle
double Area(double width, double height); // rectangle
Area(5.0); // calls circle version
Area(4.0, 3.0); // calls rectangle version
Overloads must differ in their parameter list. The following are not valid bases for overloading:
| Not Allowed | Why |
|---|---|
| Return type only | The compiler cannot distinguish int f() from void f() at a call site |
static keyword only | Linkage is not part of the function signature |
| Default arguments that create ambiguity | f(int x) and f(int x, int y=0) — calling f(1) is ambiguous |
int* arr and int arr[] are identical to the compiler and cannot be overloaded against each other.To support overloading at the linker level, the compiler encodes the parameter types into each function's symbol name — a process called name mangling. This creates unique symbols even when source names are identical.
void func(int x); // mangled as something like: _Z4funci
void func(double x); // mangled as something like: _Z4funcd
void func(std::string x); // mangled with the full type encoding
extern "C" disables mangling, making a function callable from C.When you call an overloaded function, the compiler follows these steps to pick the right one:
Candidates are ranked from best to worst. The compiler selects the highest-ranked match for each argument:
| Rank | Match Type | Notes |
|---|---|---|
| 1 (Best) | Exact argument match | Types match exactly |
| 2 | A promotion | e.g. char → int. No data loss. |
| 3 | Standard type conversion | e.g. int → double. Possible data loss. |
| 4 (Worst) | Constructor / user-defined conversion |