Introduction to Functions
Functions are reusable blocks of code that perform specific tasks. They help you organize code, avoid repetition, and make programs easier to understand, test, and maintain. Functions are fundamental to modular programming.
Function
A function is a named block of code that performs a specific task. It can accept input (parameters), process data, and return output (return value). Functions promote code reuse and modularity.
Syntax: return_type function_name(parameters) { body }
Why Use Functions?
Reusability
Write once, use many times
Modularity
Break complex problems into parts
Readability
Meaningful names explain purpose
Debugging
Test and fix isolated units
Your First Function
#include <iostream>
using namespace std;
// Function definition
void sayHello() {
cout << "Hello, World!" << endl;
}
int main() {
// Function call
sayHello(); // Output: Hello, World!
sayHello(); // Output: Hello, World!
sayHello(); // Output: Hello, World!
return 0;
}
Function Declaration & Definition
In C++, you can separate the function declaration (prototype) from its definition (implementation). This is essential for organizing larger programs and using header files.
Function Declaration (Prototype)
#include <iostream>
using namespace std;
// Function declarations (prototypes) - tell compiler about functions
int add(int a, int b); // Parameters can have names
int multiply(int, int); // Or just types
void greet(string name);
int main() {
// Now we can call functions before their definitions
cout << add(5, 3) << endl; // 8
cout << multiply(4, 7) << endl; // 28
greet("Alice"); // Hello, Alice!
return 0;
}
// Function definitions (implementations)
int add(int a, int b) {
return a + b;
}
int multiply(int x, int y) {
return x * y;
}
void greet(string name) {
cout << "Hello, " << name << "!" << endl;
}
Function Anatomy
// Function anatomy:
// return_type function_name(parameter_list) { function_body }
int calculateArea(int length, int width) {
//│ │ │ │
//│ │ └────────┴── Parameters (inputs)
//│ └───────────────────────── Function name
//└─────────────────────────────── Return type
int area = length * width; // Function body
return area; // Return statement
}
// void means no return value
void printMessage() {
cout << "No return needed" << endl;
// return; is optional for void functions
}
Declaration vs Definition
| Aspect | Declaration (Prototype) | Definition (Implementation) |
|---|---|---|
| Purpose | Tells compiler function exists | Contains actual code |
| Body | No body, ends with semicolon | Has body with curly braces |
| Location | Usually in header files (.h) | Usually in source files (.cpp) |
| Can appear | Multiple times | Only once |
Practice Questions: Declaration & Definition
Task: Write a function prototype (declaration) for a function called greet that takes a string parameter name and returns nothing.
Show Solution
void greet(string name);
Task: Write the declaration and definition separately for a function multiply that takes two integers and returns their product.
Show Solution
// Declaration (prototype)
int multiply(int a, int b);
// Definition (implementation)
int multiply(int a, int b) {
return a * b;
}
Parameters & Arguments
Parameters are variables in the function definition that receive values. Arguments are the actual values passed when calling the function. C++ supports pass by value and pass by reference.
Pass by Value
#include <iostream>
using namespace std;
// Pass by value: function receives a COPY
void doubleValue(int num) {
num = num * 2; // Only modifies the copy
cout << "Inside function: " << num << endl;
}
int main() {
int x = 10;
cout << "Before: " << x << endl; // 10
doubleValue(x); // Pass x
// Inside function: 20
cout << "After: " << x << endl; // 10 (unchanged!)
return 0;
}
Pass by Reference
#include <iostream>
using namespace std;
// Pass by reference: function receives the ORIGINAL variable
void doubleValue(int& num) { // Note the & (reference)
num = num * 2; // Modifies the original
cout << "Inside function: " << num << endl;
}
int main() {
int x = 10;
cout << "Before: " << x << endl; // 10
doubleValue(x); // Pass x by reference
// Inside function: 20
cout << "After: " << x << endl; // 20 (changed!)
return 0;
}
// Practical example: swapping two values
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// Usage:
// int x = 5, y = 10;
// swap(x, y); // Now x=10, y=5
Const Reference (Read-Only)
#include <iostream>
#include <string>
using namespace std;
// Const reference: efficient AND prevents modification
void printInfo(const string& name, const int& age) {
cout << name << " is " << age << " years old" << endl;
// name = "Changed"; // Error! Cannot modify const reference
}
// Why use const reference?
// 1. Efficiency: No copying large objects
// 2. Safety: Prevents accidental modification
// 3. Can accept literals: printInfo("Alice", 25);
int main() {
string myName = "Bob";
int myAge = 30;
printInfo(myName, myAge); // Works with variables
printInfo("Alice", 25); // Works with literals too
return 0;
}
Multiple Parameters
#include <iostream>
using namespace std;
// Multiple parameters of different types
double calculateBMI(double weight, double height) {
return weight / (height * height);
}
// Array as parameter (decays to pointer)
double average(int arr[], int size) {
double sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
return sum / size;
}
int main() {
double bmi = calculateBMI(70.0, 1.75);
cout << "BMI: " << bmi << endl;
int scores[] = {85, 90, 78, 92, 88};
double avg = average(scores, 5);
cout << "Average: " << avg << endl;
return 0;
}
Practice Questions: Parameters & Arguments
Task: Write a function swap that swaps the values of two integers using pass by reference.
Show Solution
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
Task: Write a function that takes an array and its size, and returns both minimum and maximum values through reference parameters.
Show Solution
void findMinMax(int arr[], int size, int& min, int& max) {
min = max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < min) min = arr[i];
if (arr[i] > max) max = arr[i];
}
}
// Usage:
int nums[] = {5, 2, 8, 1, 9};
int minVal, maxVal;
findMinMax(nums, 5, minVal, maxVal);
// minVal = 1, maxVal = 9
Task: Write a function that increments each element of an array by a given value. The array should be modified in place.
Show Solution
void incrementAll(int arr[], int size, int value) {
for (int i = 0; i < size; i++) {
arr[i] += value;
}
}
// Usage:
int nums[] = {1, 2, 3, 4, 5};
incrementAll(nums, 5, 10);
// nums is now {11, 12, 13, 14, 15}
Return Types & Values
Functions can return values of any type. The return type must be declared, and
the returned value must match (or be convertible to) that type. Use
void when no return value is needed.
#include <iostream>
#include <string>
using namespace std;
// Return int
int square(int n) {
return n * n;
}
// Return double
double divide(double a, double b) {
if (b == 0) {
return 0.0; // Handle division by zero
}
return a / b;
}
// Return bool
bool isEven(int num) {
return num % 2 == 0;
}
// Return string
string getGrade(int score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
if (score >= 60) return "D";
return "F";
}
// Return void (no return value)
void printStars(int count) {
for (int i = 0; i < count; i++) {
cout << "*";
}
cout << endl;
// return; is optional
}
int main() {
cout << "5 squared: " << square(5) << endl; // 25
cout << "10 / 3: " << divide(10, 3) << endl; // 3.33...
cout << "Is 4 even? " << isEven(4) << endl; // 1 (true)
cout << "Grade for 85: " << getGrade(85) << endl; // B
printStars(5); // *****
return 0;
}
Multiple Return Statements
#include <iostream>
using namespace std;
// Early return for validation
int factorial(int n) {
if (n < 0) {
return -1; // Error indicator
}
if (n == 0 || n == 1) {
return 1; // Base case
}
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
// Finding values
int findMax(int a, int b, int c) {
if (a >= b && a >= c) return a;
if (b >= a && b >= c) return b;
return c;
}
int main() {
cout << "5! = " << factorial(5) << endl; // 120
cout << "Max: " << findMax(10, 25, 15) << endl; // 25
return 0;
}
Returning References
#include <iostream>
using namespace std;
// Return reference to modify external variable
int& getElement(int arr[], int index) {
return arr[index]; // Returns reference to array element
}
// Return const reference for read-only access
const string& getLonger(const string& a, const string& b) {
return (a.length() > b.length()) ? a : b;
}
int main() {
int numbers[] = {10, 20, 30, 40, 50};
// Can use returned reference to modify
getElement(numbers, 2) = 100; // Change arr[2] to 100
cout << numbers[2] << endl; // 100
string s1 = "Hello";
string s2 = "World!";
cout << getLonger(s1, s2) << endl; // World!
return 0;
}
Practice Questions: Return Types
Task: Write a function absoluteValue that returns the absolute value of an integer.
Show Solution
int absoluteValue(int n) {
if (n < 0) {
return -n;
}
return n;
}
Task: Write a function isPrime that returns true if a number is prime, false otherwise.
Show Solution
bool isPrime(int n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0) return false;
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) return false;
}
return true;
}
Function Overloading
Function overloading allows multiple functions to have the same name but different parameters. The compiler selects the correct version based on the arguments passed. This is a key feature of C++ polymorphism.
#include <iostream>
#include <string>
using namespace std;
// Overloaded print functions - same name, different parameters
void print(int value) {
cout << "Integer: " << value << endl;
}
void print(double value) {
cout << "Double: " << value << endl;
}
void print(string value) {
cout << "String: " << value << endl;
}
void print(int a, int b) {
cout << "Two integers: " << a << ", " << b << endl;
}
int main() {
print(42); // Calls print(int)
print(3.14); // Calls print(double)
print("Hello"); // Calls print(string)
print(10, 20); // Calls print(int, int)
return 0;
}
/* Output:
Integer: 42
Double: 3.14
String: Hello
Two integers: 10, 20
*/
Overloading Rules
#include <iostream>
using namespace std;
// Valid overloads: different parameter types or count
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;
}
// INVALID: Cannot overload by return type alone
// int getValue();
// double getValue(); // Error! Same parameters
// INVALID: Cannot overload by parameter names only
// void func(int x);
// void func(int y); // Error! Same signature
int main() {
cout << add(5, 3) << endl; // 8 (int version)
cout << add(2.5, 3.7) << endl; // 6.2 (double version)
cout << add(1, 2, 3) << endl; // 6 (three-param version)
return 0;
}
Practical Example: Area Calculator
#include <iostream>
using namespace std;
const double PI = 3.14159;
// Area of square
double area(double side) {
return side * side;
}
// Area of rectangle
double area(double length, double width) {
return length * width;
}
// Area of circle (using different parameter name doesn't help distinguish,
// but different type would - here we use a flag)
double area(double radius, bool isCircle) {
if (isCircle) {
return PI * radius * radius;
}
return radius * radius;
}
// Area of triangle
double area(double base, double height, char type) {
return 0.5 * base * height;
}
int main() {
cout << "Square (5): " << area(5.0) << endl;
cout << "Rectangle (4x6): " << area(4.0, 6.0) << endl;
cout << "Circle (r=3): " << area(3.0, true) << endl;
cout << "Triangle (b=4, h=5): " << area(4.0, 5.0, 't') << endl;
return 0;
}
Practice Questions: Function Overloading
Task: Create three overloaded display functions that print an int, a double, and a string respectively.
Show Solution
void display(int value) {
cout << "Integer: " << value << endl;
}
void display(double value) {
cout << "Double: " << value << endl;
}
void display(string value) {
cout << "String: " << value << endl;
}
Task: Create overloaded max functions that find the maximum of 2 integers and 3 integers.
Show Solution
int max(int a, int b) {
return (a > b) ? a : b;
}
int max(int a, int b, int c) {
return max(max(a, b), c);
}
Default Arguments
Default arguments provide fallback values for parameters when no argument is supplied. They must be specified from right to left in the parameter list.
#include <iostream>
#include <string>
using namespace std;
// Default argument example
void greet(string name = "Guest", string greeting = "Hello") {
cout << greeting << ", " << name << "!" << endl;
}
int main() {
greet(); // Hello, Guest!
greet("Alice"); // Hello, Alice!
greet("Bob", "Hi"); // Hi, Bob!
return 0;
}
Default Argument Rules
#include <iostream>
using namespace std;
// Rule 1: Defaults must be from right to left
void func1(int a, int b = 10, int c = 20); // Valid
// void func2(int a = 5, int b, int c = 20); // Error! b has no default
// Rule 2: Declare defaults only once (usually in prototype)
void display(int x, int y = 100); // Declare default here
int main() {
func1(1); // a=1, b=10, c=20
func1(1, 2); // a=1, b=2, c=20
func1(1, 2, 3); // a=1, b=2, c=3
display(5); // x=5, y=100
display(5, 50); // x=5, y=50
return 0;
}
// Don't repeat default in definition
void func1(int a, int b, int c) {
cout << a << ", " << b << ", " << c << endl;
}
void display(int x, int y) { // No default here
cout << x << ", " << y << endl;
}
Practical Example: Formatting Output
#include <iostream>
#include <iomanip>
using namespace std;
void printLine(int length = 40, char symbol = '-') {
for (int i = 0; i < length; i++) {
cout << symbol;
}
cout << endl;
}
void printCentered(string text, int width = 50, char fill = ' ') {
int padding = (width - text.length()) / 2;
cout << string(padding, fill) << text
<< string(width - padding - text.length(), fill) << endl;
}
int main() {
printLine(); // 40 dashes
printLine(20); // 20 dashes
printLine(30, '*'); // 30 asterisks
printCentered("WELCOME");
printCentered("MENU", 30);
printCentered("Title", 40, '=');
return 0;
}
Practice Questions: Default Arguments
Task: Write a function power that calculates base^exp, with a default exponent of 2 (square).
Show Solution
double power(double base, int exp = 2) {
double result = 1;
for (int i = 0; i < exp; i++) {
result *= base;
}
return result;
}
// Usage:
power(5); // 25 (5^2)
power(2, 10); // 1024 (2^10)
Task: Write a function repeatString that repeats a string n times with a separator. Default: repeat 1 time with space separator.
Show Solution
string repeatString(string text, int times = 1, string separator = " ") {
string result = text;
for (int i = 1; i < times; i++) {
result += separator + text;
}
return result;
}
// Usage:
repeatString("Hi"); // "Hi"
repeatString("Hi", 3); // "Hi Hi Hi"
repeatString("Hi", 3, "-"); // "Hi-Hi-Hi"
Inline Functions
Inline functions suggest to the compiler to insert the function code directly at the call site, avoiding function call overhead. They're ideal for small, frequently-called functions.
#include <iostream>
using namespace std;
// Inline function - compiler may insert code at call site
inline int max(int a, int b) {
return (a > b) ? a : b;
}
inline int square(int n) {
return n * n;
}
inline bool isPositive(int num) {
return num > 0;
}
int main() {
int x = 10, y = 20;
// Compiler may replace this with: (x > y) ? x : y
cout << "Max: " << max(x, y) << endl;
// May become: 5 * 5
cout << "Square of 5: " << square(5) << endl;
if (isPositive(x)) {
cout << x << " is positive" << endl;
}
return 0;
}
When to Use Inline
- Very small functions (1-3 lines)
- Frequently called functions
- Simple getters/setters
- Simple mathematical operations
- Large functions (increases code size)
- Functions with loops
- Recursive functions
- Functions with static variables
Inline in Classes
#include <iostream>
using namespace std;
class Rectangle {
private:
double width, height;
public:
// Methods defined inside class are implicitly inline
double getWidth() { return width; }
double getHeight() { return height; }
void setWidth(double w) { width = w; }
void setHeight(double h) { height = h; }
double area() { return width * height; }
};
int main() {
Rectangle rect;
rect.setWidth(5);
rect.setHeight(3);
cout << "Area: " << rect.area() << endl; // 15
return 0;
}
inline keyword is a suggestion, not a
command. Modern compilers may inline functions automatically or ignore the
keyword based on optimization settings.
Practice Questions: Inline Functions
Task: Write inline functions min and max that return the smaller/larger of two integers.
Show Solution
inline int min(int a, int b) {
return (a < b) ? a : b;
}
inline int max(int a, int b) {
return (a > b) ? a : b;
}
Task: Create a Circle class with inline getter/setter for radius and inline methods for area and circumference.
Show Solution
class Circle {
private:
double radius;
public:
// Implicitly inline (defined in class)
double getRadius() { return radius; }
void setRadius(double r) { radius = r; }
double area() { return 3.14159 * radius * radius; }
double circumference() { return 2 * 3.14159 * radius; }
};
Recursion
Recursion is when a function calls itself. It's useful for problems that can be broken down into smaller, similar subproblems. Every recursive function needs a base case to stop the recursion.
#include <iostream>
using namespace std;
// Factorial using recursion
int factorial(int n) {
// Base case: stop recursion
if (n == 0 || n == 1) {
return 1;
}
// Recursive case: call itself
return n * factorial(n - 1);
}
// How it works:
// factorial(5)
// = 5 * factorial(4)
// = 5 * 4 * factorial(3)
// = 5 * 4 * 3 * factorial(2)
// = 5 * 4 * 3 * 2 * factorial(1)
// = 5 * 4 * 3 * 2 * 1
// = 120
int main() {
cout << "5! = " << factorial(5) << endl; // 120
cout << "0! = " << factorial(0) << endl; // 1
return 0;
}
More Recursion Examples
#include <iostream>
using namespace std;
// Fibonacci sequence
int fibonacci(int n) {
if (n <= 1) return n; // Base case
return fibonacci(n - 1) + fibonacci(n - 2); // Recursive case
}
// Sum of digits
int sumDigits(int n) {
if (n == 0) return 0; // Base case
return (n % 10) + sumDigits(n / 10);
}
// Power function
int power(int base, int exp) {
if (exp == 0) return 1; // Base case
return base * power(base, exp - 1);
}
// Count down
void countdown(int n) {
if (n == 0) {
cout << "Blast off!" << endl;
return; // Base case
}
cout << n << "..." << endl;
countdown(n - 1); // Recursive call
}
int main() {
cout << "Fibonacci(7): " << fibonacci(7) << endl; // 13
cout << "Sum of digits(12345): " << sumDigits(12345) << endl; // 15
cout << "2^10: " << power(2, 10) << endl; // 1024
countdown(5);
return 0;
}
- Always have a base case to prevent infinite recursion
- Each recursive call uses stack memory (risk of stack overflow)
- Can be slower than iterative solutions due to function call overhead
- Consider iterative alternatives for performance-critical code
Practice Questions: Recursion
Task: Write a recursive function sum that calculates the sum of all integers from 1 to n.
Show Solution
int sum(int n) {
if (n <= 0) return 0; // Base case
return n + sum(n - 1); // Recursive case
}
// sum(5) = 5 + 4 + 3 + 2 + 1 = 15
Task: Write a recursive function reverse that reverses a string.
Show Solution
string reverse(string s) {
if (s.length() <= 1) return s; // Base case
return reverse(s.substr(1)) + s[0]; // Recursive case
}
// reverse("hello") = "olleh"
Task: Write a recursive function gcd to find the Greatest Common Divisor of two numbers using Euclidean algorithm.
Show Solution
int gcd(int a, int b) {
if (b == 0) return a; // Base case
return gcd(b, a % b); // Recursive case
}
// gcd(48, 18) = gcd(18, 12) = gcd(12, 6) = gcd(6, 0) = 6
Key Takeaways
Function Basics
Declaration, definition, return type, parameters
Pass by Value/Reference
Copy vs original; use & for modification
Overloading
Same name, different parameters
Default Arguments
Fallback values from right to left
Inline Functions
Reduce call overhead for small functions
Recursion
Function calls itself; needs base case
Knowledge Check
Quick Quiz
Test what you have learned about C++ functions