README
Function Basics in C++
Table of Contents
- •Introduction
- •What are Functions?
- •Function Declaration and Definition
- •Function Components
- •Return Types
- •The main() Function
- •Function Call Mechanism
- •Function Scope
- •Header Files and Function Organization
- •Default Arguments
- •Function Best Practices
- •Summary
Introduction
Functions are the fundamental building blocks of C++ programs. They allow you to:
- •Organize code into reusable, manageable units
- •Avoid repetition by writing code once and calling it multiple times
- •Abstract complexity by hiding implementation details
- •Improve readability through descriptive function names
- •Enable testing by isolating functionality
Why Functions Matter
// Without functions (repetitive, hard to maintain)
int sum1 = 0;
for (int i = 0; i < 10; i++) sum1 += arr1[i];
int sum2 = 0;
for (int i = 0; i < 20; i++) sum2 += arr2[i];
// With functions (clean, reusable)
int sum1 = calculateSum(arr1, 10);
int sum2 = calculateSum(arr2, 20);
What are Functions?
A function is a named block of code that:
- •Performs a specific task
- •Can accept input (parameters)
- •Can produce output (return value)
- •Can be called from other parts of the program
Anatomy of a Function
// Return Type Function Name Parameters
// ↓ ↓ ↓
int add (int a, int b)
{ // ← Function Body Begins
return a + b; // ← Statement(s)
} // ← Function Body Ends
Types of Functions
| Type | Description | Example |
|---|---|---|
| User-defined | Created by programmer | calculateTax() |
| Library | Provided by C++ standard library | sqrt(), strlen() |
| Member | Belongs to a class | myObject.display() |
| Free | Not part of any class | printMessage() |
Function Declaration and Definition
Declaration (Prototype)
A declaration tells the compiler about a function's:
- •Name
- •Return type
- •Parameters (types and number)
// Function declarations (prototypes)
int add(int a, int b); // Parameter names optional
double calculateArea(double); // Parameter name omitted
void printMessage(); // No parameters
string formatName(string, string); // Multiple parameters
Definition (Implementation)
A definition provides the actual code:
// Function definition
int add(int a, int b) {
return a + b;
}
double calculateArea(double radius) {
return 3.14159 * radius * radius;
}
void printMessage() {
cout << "Hello, World!" << endl;
}
Declaration vs Definition
// Declaration (usually in header file .h)
int multiply(int x, int y);
// Definition (usually in source file .cpp)
int multiply(int x, int y) {
return x * y;
}
Why Separate Declaration?
- •Forward declaration: Use function before defining it
- •Multiple files: Share functions across files
- •Header files: Organize interfaces
- •Compilation: Compiler needs to know function signature
Function Components
1. Return Type
Specifies what type of value the function returns:
int getAge() // Returns integer
double getPrice() // Returns double
string getName() // Returns string
bool isValid() // Returns boolean
void printInfo() // Returns nothing
2. Function Name
Rules for function names:
- •Start with letter or underscore
- •Contains letters, digits, underscores
- •Case-sensitive
- •Cannot be a reserved keyword
// Good names (descriptive)
int calculateTotal()
bool isValidEmail()
void processPayment()
string formatDate()
// Poor names (avoid)
int calc() // Too short
int x() // Meaningless
int foo123() // Not descriptive
3. Parameters (Arguments)
Data passed into the function:
// No parameters
void greet() {
cout << "Hello!" << endl;
}
// One parameter
void printNumber(int n) {
cout << "Number: " << n << endl;
}
// Multiple parameters
int add(int a, int b, int c) {
return a + b + c;
}
// Mixed types
void displayInfo(string name, int age, double salary) {
cout << name << " is " << age << " years old." << endl;
}
4. Function Body
The code block that executes:
int factorial(int n) {
// ← Body starts
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
// ← Body ends
}
5. Return Statement
Exits the function and optionally returns a value:
int max(int a, int b) {
if (a > b) {
return a; // Early return
}
return b; // Normal return
}
void printPositive(int n) {
if (n <= 0) {
return; // Early exit (no value)
}
cout << n << endl;
}
Return Types
Fundamental Types
int getInteger() { return 42; }
double getDouble() { return 3.14; }
char getChar() { return 'A'; }
bool getBool() { return true; }
void - No Return Value
void printGreeting() {
cout << "Hello!" << endl;
// No return statement needed (implicit return)
}
void logError(string message) {
cerr << "Error: " << message << endl;
return; // Optional explicit return
}
Returning Objects
#include <string>
#include <vector>
string getFullName(string first, string last) {
return first + " " + last;
}
vector<int> getEvenNumbers(int max) {
vector<int> evens;
for (int i = 2; i <= max; i += 2) {
evens.push_back(i);
}
return evens; // Returns copy (or moved with RVO)
}
auto Return Type (C++14)
auto add(int a, int b) {
return a + b; // Deduced as int
}
auto getPI() {
return 3.14159; // Deduced as double
}
Trailing Return Type (C++11)
auto add(int a, double b) -> double {
return a + b;
}
// Useful with templates
template<typename T, typename U>
auto multiply(T a, U b) -> decltype(a * b) {
return a * b;
}
The main() Function
The entry point of every C++ program.
Standard Forms
// Form 1: No parameters
int main() {
// Program code
return 0; // Success
}
// Form 2: With command-line arguments
int main(int argc, char* argv[]) {
// argc = argument count
// argv = argument values
return 0;
}
// Alternative Form 2
int main(int argc, char** argv) {
return 0;
}
Return Values
| Return Value | Meaning |
|---|---|
0 | Successful execution |
| Non-zero | Error occurred |
EXIT_SUCCESS | Macro for success (typically 0) |
EXIT_FAILURE | Macro for failure (typically 1) |
#include <cstdlib>
int main() {
if (/* error condition */) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Command-Line Arguments
int main(int argc, char* argv[]) {
cout << "Number of arguments: " << argc << endl;
for (int i = 0; i < argc; i++) {
cout << "argv[" << i << "] = " << argv[i] << endl;
}
return 0;
}
// Run: ./program arg1 arg2 arg3
// Output:
// Number of arguments: 4
// argv[0] = ./program
// argv[1] = arg1
// argv[2] = arg2
// argv[3] = arg3
Function Call Mechanism
How Function Calls Work
- •Push arguments onto the call stack
- •Push return address (where to continue after function)
- •Jump to function code
- •Execute function body
- •Push return value (if any)
- •Pop stack frame and return
Call Stack Visualization
Before call: During call: After return:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ main() │ │ main() │ │ main() │
│ │ │ │ │ │
│ int x = 5 │ │ int x = 5 │ │ int x = 5 │
│ int y = 3 │ │ int y = 3 │ │ int y = 3 │
│ │ ├──────────────┤ │ int z = 8 │
│ add(x, y) │ │ add() │ │ │
│ │ │ a = 5 │ └──────────────┘
└──────────────┘ │ b = 3 │
│ return 8 │
└──────────────┘
Calling a Function
int add(int a, int b) {
return a + b;
}
int main() {
// Direct call
int result1 = add(5, 3);
// Call with variables
int x = 10, y = 20;
int result2 = add(x, y);
// Nested calls
int result3 = add(add(1, 2), add(3, 4)); // add(3, 7) = 10
// Expression in argument
int result4 = add(x * 2, y - 5); // add(20, 15)
return 0;
}
Function Scope
Local Variables
Variables declared inside a function exist only within that function:
void example() {
int local = 10; // Local to example()
cout << local; // OK
}
int main() {
example();
// cout << local; // ERROR! local is not visible here
return 0;
}
Scope Hierarchy
int global = 100; // Global scope
void outer() {
int outer_var = 50; // Local to outer()
{ // Inner block
int inner_var = 25; // Local to this block
cout << global; // OK - global visible
cout << outer_var; // OK - outer scope visible
cout << inner_var; // OK - current scope
}
// cout << inner_var; // ERROR - out of scope
}
Static Local Variables
Persist between function calls:
void counter() {
static int count = 0; // Initialized once
count++;
cout << "Called " << count << " times" << endl;
}
int main() {
counter(); // Called 1 times
counter(); // Called 2 times
counter(); // Called 3 times
return 0;
}
Global Variables
Accessible from any function (use sparingly):
int globalCounter = 0; // Global
void increment() {
globalCounter++;
}
void print() {
cout << globalCounter << endl;
}
int main() {
increment();
increment();
print(); // Output: 2
return 0;
}
Header Files and Function Organization
Header File (.h or .hpp)
Contains declarations:
// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
// Function declarations
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
double divide(int a, int b);
#endif
Source File (.cpp)
Contains definitions:
// math_utils.cpp
#include "math_utils.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
double divide(int a, int b) {
if (b == 0) throw runtime_error("Division by zero");
return static_cast<double>(a) / b;
}
Main File
Uses the functions:
// main.cpp
#include <iostream>
#include "math_utils.h"
using namespace std;
int main() {
cout << add(5, 3) << endl; // 8
cout << multiply(4, 7) << endl; // 28
return 0;
}
Compilation
g++ -c math_utils.cpp -o math_utils.o
g++ -c main.cpp -o main.o
g++ math_utils.o main.o -o program
# Or all at once:
g++ math_utils.cpp main.cpp -o program
Default Arguments
Provide default values for parameters:
Syntax
// Declaration with defaults
void greet(string name = "Guest", int times = 1);
// Definition (defaults only in declaration OR definition, not both)
void greet(string name, int times) {
for (int i = 0; i < times; i++) {
cout << "Hello, " << name << "!" << endl;
}
}
Usage
int main() {
greet(); // Hello, Guest! (uses both defaults)
greet("Alice"); // Hello, Alice! (uses times default)
greet("Bob", 3); // Hello, Bob! (3 times, no defaults)
return 0;
}
Rules for Default Arguments
- •Right-to-left: Defaults must be rightmost parameters
- •No gaps: Can't skip parameters when calling
- •Declare once: Either in declaration or definition
// Correct
void func(int a, int b = 5, int c = 10);
// WRONG - default before non-default
// void func(int a = 1, int b, int c = 10);
// WRONG - gap in defaults
// void func(int a, int b = 5, int c);
Function Best Practices
1. Single Responsibility
Each function should do one thing well:
// Bad - too many responsibilities
void processUserData(User& user) {
validateEmail(user.email);
hashPassword(user.password);
saveToDatabase(user);
sendWelcomeEmail(user);
}
// Good - single responsibility
void validateUser(User& user);
void securePassword(User& user);
void saveUser(User& user);
void notifyUser(User& user);
2. Descriptive Names
// Bad
int c(int a, int b);
void p(string s);
// Good
int calculateSum(int first, int second);
void printMessage(string message);
3. Keep Functions Short
Aim for 20-30 lines maximum. If longer, consider breaking into smaller functions.
4. Limit Parameters
// Bad - too many parameters
void createUser(string first, string last, string email,
int age, string address, string city,
string country, string phone);
// Good - use a struct/class
struct UserInfo {
string firstName, lastName, email;
int age;
Address address;
string phone;
};
void createUser(const UserInfo& info);
5. Consistent Naming Conventions
| Style | Example | Use Case |
|---|---|---|
| camelCase | calculateTotal() | Member functions |
| snake_case | calculate_total() | Free functions |
| PascalCase | CalculateTotal() | Some teams prefer this |
6. Document Complex Functions
/**
* Calculates the factorial of a non-negative integer.
*
* @param n Non-negative integer (0 to 12 for int result)
* @return n! (factorial of n)
* @throws invalid_argument if n is negative
* @note Returns 1 for n=0 (0! = 1 by definition)
*/
int factorial(int n);
Summary
Key Concepts
- •Functions encapsulate reusable code blocks
- •Declaration specifies interface (prototype)
- •Definition provides implementation
- •Parameters pass data into functions
- •Return values pass data out of functions
- •Scope determines variable visibility
- •Header files organize declarations
Function Syntax Quick Reference
// Declaration
returnType functionName(paramType1 param1, paramType2 param2);
// Definition
returnType functionName(paramType1 param1, paramType2 param2) {
// body
return value; // if not void
}
// void function
void functionName() {
// body
// no return needed (or use: return;)
}
// Default arguments
void func(int a, int b = 10, int c = 20);
// auto return type (C++14)
auto func(int a, int b) {
return a + b;
}
Common Patterns
// Getter function
int getValue() { return value; }
// Setter function
void setValue(int v) { value = v; }
// Predicate function (returns bool)
bool isEmpty() { return size == 0; }
// Factory function
Object createObject(params) { return Object(params); }
// Utility/Helper function
string formatCurrency(double amount) {
return "$" + to_string(amount);
}
Practice Files
- •examples.cpp: Comprehensive code examples demonstrating all function concepts
- •exercises.cpp: Practice problems from beginner to advanced
Compilation
g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
g++ -std=c++17 -Wall -Wextra exercises.cpp -o exercises
./examples
./exercises