When should I pass by const reference?
For large objects you will not modify—avoids copy cost while preventing accidental changes.
Master C++ functions: function declaration, definition, parameters, return values, call by value/reference, passing arrays, recursion, and best practices for modular programming.
Declaration & Definition
Value vs Reference
1D & 2D Passing
Self-calling Functions
Functions structure code into reusable units. Master prototypes, scope, parameter passing, overloading, and inline hints—the building blocks before recursion and OOP methods.
Every interview uses functions. Parameter passing and overloading distinguish candidates who understand memory and APIs.
Functions are fundamental building blocks in C++ that allow you to organize code into reusable, modular units. They enable code reuse, improve readability, and make debugging easier.
The following table compares different function types and parameter passing methods in C++:
| Function Type | Syntax Example | When to Use | Characteristics |
|---|---|---|---|
| Basic Function | int add(int a, int b) { } | Simple operations, calculations | Returns value, has parameters |
| Void Function | void printMsg() { } | Actions without return value | No return value, may have parameters |
| Call by Value | void func(int x) { } | Don't modify original values | Creates copy, original unchanged |
| Call by Reference | void func(int &x) { } | Modify original values | Works on original, no copy |
| Array Parameter | void func(int arr[], int size) { } | Process array elements | Passes address, modifies original |
| Recursive Function | int factorial(int n) { } | Problems with recursive nature | Calls itself, base case needed |
A function consists of a declaration (prototype) and definition. The declaration specifies the function's interface, while the definition provides the implementation.
// FUNCTION DECLARATION (Prototype)
return_type function_name(parameter_list);
// FUNCTION DEFINITION
return_type function_name(parameter_list) {
// Function body
// Statements
return value; // if return_type is not void
}
void greet() {
cout << "Hello";
}int add(int a, int b) {
return a + b;
}int sum = add(3, 5); // 8int multiply(int, int);
// ... later definitionvoid print(int n = 10);inline int sq(int x) { return x*x; }C++ supports two main ways to pass parameters: call by value (creates copy) and call by reference (works on original).
// CALL BY VALUE (creates copy)
void func(int x) {
x = 100; // Changes local copy only
}
// CALL BY REFERENCE (works on original)
void func(int &x) {
x = 100; // Changes original variable
}
// CALL BY CONST REFERENCE (read-only, efficient)
void func(const int &x) {
// Can read x but not modify
}
void f(int x) { x = 10; }
int a = 5; f(a); // a still 5void f(int& x) { x = 10; }
int a = 5; f(a); // a is 10void f(const int& x) {
cout << x; // read-only
}void f(int* p) { *p = 10; }
f(&a);void swap(int& a, int& b) {
int t = a; a = b; b = t;
}int& maxRef(int& a, int& b) {
return (a > b) ? a : b;
}Arrays are always passed by reference to functions. For 1D arrays, you need to pass the size separately. For 2D arrays, you must specify column size.
// 1D ARRAY PARAMETER
void processArray(int arr[], int size) {
// arr is pointer to first element
}
// 2D ARRAY PARAMETER - MUST SPECIFY COLUMNS
void processMatrix(int matrix[][COLS], int rows) {
// Column size must be specified
}
// USING POINTER NOTATION
void processArray(int *arr, int size) {
// Same as arr[]
}
void print(int arr[], int n);int sum(int arr[], int n) {
int s = 0;
for (int i = 0; i < n; i++) s += arr[i];
return s;
}void scale(int arr[], int n, int k) {
for (int i = 0; i < n; i++) arr[i] *= k;
}void show(int m[][3], int rows);int maxVal(int arr[], int n);void print(const int arr[], int n);int arr[] and int *arr are equivalent in parametersFunction overloading allows multiple functions with the same name but different parameters. The compiler distinguishes them based on parameter count or types.
// DIFFERENT PARAMETER COUNT
int add(int a, int b);
int add(int a, int b, int c);
// DIFFERENT PARAMETER TYPES
int add(int a, int b);
double add(double a, double b);
// DIFFERENT PARAMETER ORDER
void print(int id, string name);
void print(string name, int id);
int add(int a, int b) {
return a + b;
}double add(double a, double b) {
return a + b;
}int add(int a, int b, int c) {
return a + b + c;
}void print(int x) { cout << x; }void print(string s) { cout << s; }add(1, 2); // int version
add(1.5, 2.5); // double versionA recursive function calls itself to solve a problem. It must have a base case (stopping condition) and a recursive case (calls itself).
return_type function(parameters) {
// BASE CASE (stopping condition)
if (stopping_condition) {
return base_value;
}
// RECURSIVE CASE (call itself)
return function(modified_parameters);
}
int fact(int n) {
if (n <= 1) return 1;
return n * fact(n - 1);
}int fib(int n) {
if (n <= 1) return n;
return fib(n-1) + fib(n-2);
}int sum(int n) {
if (n == 0) return 0;
return n + sum(n - 1);
}int power(int b, int e) {
if (e == 0) return 1;
return b * power(b, e - 1);
}int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}// always define stopping conditionint square(int x) { return x * x; }bool isPrime(int n); // not check()void log(const string& msg);int add(int a, int b) {
return a + b; // no globals
}int getMax(int a, int b) {
return (a > b) ? a : b;
}// arr: input array, n: size (n > 0)
int sum(int arr[], int n);For large objects you will not modify—avoids copy cost while preventing accidental changes.
No—signature includes name and parameter types only, not return type.
Hint to embed code at call site; modern compilers decide; still need ODR rules for multiple translation units.