C++ Functions: Complete Guide with Examples
Master C++ functions: function declaration, definition, parameters, return values, call by value/reference, passing arrays, recursion, and best practices for modular programming.
Function Basics
Declaration & Definition
Parameters
Value vs Reference
Arrays
1D & 2D Passing
Recursion
Self-calling Functions
Introduction to Functions
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.
Why Use Functions?
- Code Reusability
- Modular Programming
- Improved Readability
- Easier Debugging
- Code Organization
- Team Collaboration
Function Components
- Return Type: Data type returned
- Function Name: Identifier
- Parameters: Input values
- Function Body: Implementation
- Prototype: Declaration
Function Execution Flow
C++ Functions Overview
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 |
1. Function Basics: Declaration & Definition
A function consists of a declaration (prototype) and definition. The declaration specifies the function's interface, while the definition provides the implementation.
Function Components
// 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
}
#include <iostream>
using namespace std;
// Function prototypes (declarations)
int add(int a, int b);
void printMessage(string message);
int getMax(int x, int y);
double calculateAverage(double num1, double num2);
bool isEven(int number);
int main() {
// Example 1: Simple addition function
cout << "=== Example 1: Addition Function ===" << endl;
int result = add(10, 20);
cout << "10 + 20 = " << result << endl << endl;
// Example 2: Void function
cout << "=== Example 2: Void Function ===" << endl;
printMessage("Hello from C++ Functions!");
cout << endl;
// Example 3: Maximum function
cout << "=== Example 3: Maximum Function ===" << endl;
int a = 15, b = 25;
cout << "Maximum of " << a << " and " << b << " is: "
<< getMax(a, b) << endl << endl;
// Example 4: Average function
cout << "=== Example 4: Average Function ===" << endl;
double avg = calculateAverage(85.5, 92.0);
cout << "Average of 85.5 and 92.0 is: " << avg << endl << endl;
// Example 5: Boolean function
cout << "=== Example 5: Boolean Function ===" << endl;
int num = 7;
if (isEven(num)) {
cout << num << " is even" << endl;
} else {
cout << num << " is odd" << endl;
}
return 0;
}
// Function definitions
int add(int a, int b) {
return a + b;
}
void printMessage(string message) {
cout << "Message: " << message << endl;
}
int getMax(int x, int y) {
if (x > y) {
return x;
} else {
return y;
}
}
double calculateAverage(double num1, double num2) {
return (num1 + num2) / 2.0;
}
bool isEven(int number) {
return (number % 2 == 0);
}
- Use descriptive names (calculateAverage, isValidInput)
- CamelCase or snake_case (calculateAverage or calculate_average)
- Start with verb for actions (print, calculate, validate)
- Boolean functions start with "is", "has", "can" (isValid, hasPermission)
- Missing return statement in non-void function
- Function prototype mismatch with definition
- Forgetting semicolon after prototype
- Redeclaring parameters in function body
2. Parameter Passing: Call by Value vs Reference
C++ supports two main ways to pass parameters: call by value (creates copy) and call by reference (works on original).
Parameter Passing Syntax
// 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
}
#include <iostream>
using namespace std;
// Function prototypes
void callByValue(int x);
void callByReference(int &x);
void swapValues(int &a, int &b);
void printInfo(const string &name, int age); // const reference
void modifyArray(int arr[], int size);
int main() {
cout << "=== PARAMETER PASSING DEMONSTRATION ===" << endl << endl;
// Example 1: Call by Value
cout << "Example 1: Call by Value" << endl;
int num1 = 10;
cout << "Before callByValue: num1 = " << num1 << endl;
callByValue(num1);
cout << "After callByValue: num1 = " << num1 << endl;
cout << "(Original unchanged - value was copied)" << endl << endl;
// Example 2: Call by Reference
cout << "Example 2: Call by Reference" << endl;
int num2 = 20;
cout << "Before callByReference: num2 = " << num2 << endl;
callByReference(num2);
cout << "After callByReference: num2 = " << num2 << endl;
cout << "(Original changed - reference used)" << endl << endl;
// Example 3: Swap values using reference
cout << "Example 3: Swapping Values" << endl;
int x = 5, y = 10;
cout << "Before swap: x = " << x << ", y = " << y << endl;
swapValues(x, y);
cout << "After swap: x = " << x << ", y = " << y << endl << endl;
// Example 4: Const reference for efficiency
cout << "Example 4: Const Reference" << endl;
string studentName = "Alice";
int studentAge = 21;
printInfo(studentName, studentAge);
cout << "(String passed by const reference to avoid copying)" << endl << endl;
// Example 5: Default arguments
cout << "Example 5: Default Arguments" << endl;
cout << "Area of rectangle 5x3: " << calculateArea(5, 3) << endl;
cout << "Area of square side 4: " << calculateArea(4) << endl;
cout << "Area with no arguments: " << calculateArea() << endl << endl;
// Example 6: Array passing (always by reference)
cout << "Example 6: Array Parameter" << endl;
int numbers[] = {1, 2, 3, 4, 5};
int size = 5;
cout << "Original array: ";
for (int i = 0; i < size; i++) {
cout << numbers[i] << " ";
}
cout << endl;
modifyArray(numbers, size);
cout << "Modified array: ";
for (int i = 0; i < size; i++) {
cout << numbers[i] << " ";
}
cout << endl;
return 0;
}
// Function definitions
void callByValue(int x) {
x = 100; // Changes local copy only
cout << "Inside callByValue: x = " << x << endl;
}
void callByReference(int &x) {
x = 100; // Changes original variable
cout << "Inside callByReference: x = " << x << endl;
}
void swapValues(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void printInfo(const string &name, int age) {
// name is const reference - can't modify, but efficient for strings
cout << "Student: " << name << ", Age: " << age << endl;
}
// Function with default arguments
double calculateArea(double length = 1.0, double width = 1.0) {
return length * width;
}
void modifyArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2; // Double each element
}
}
Call by Value
- Creates copy of argument
- Original unchanged
- Good for small data types
- Inefficient for large objects
- Use when don't need to modify
Call by Reference
- Works on original data
- Can modify original
- Efficient for large objects
- Use & symbol in parameters
- Use when need to modify
Const Reference
- Read-only access
- Efficient, no copying
- Cannot modify data
- Best for large read-only data
- Use const keyword
3. Passing Arrays to Functions: 1D & 2D Arrays
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.
Array Parameter Syntax
// 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[]
}
#include <iostream>
#include <iomanip>
using namespace std;
// Function prototypes for 1D arrays
void printArray(int arr[], int size);
int findMax(int arr[], int size);
int findSum(int arr[], int size);
void reverseArray(int arr[], int size);
void sortArray(int arr[], int size);
// Function prototypes for 2D arrays
const int COLS = 4; // Must be constant for 2D array parameters
void printMatrix(int matrix[][COLS], int rows);
int sumMatrix(int matrix[][COLS], int rows);
void transposeMatrix(int matrix[][COLS], int rows);
int main() {
cout << "=== PASSING ARRAYS TO FUNCTIONS ===" << endl << endl;
// 1D ARRAY EXAMPLES
cout << "=== 1D ARRAY OPERATIONS ===" << endl;
int numbers[] = {45, 12, 89, 34, 67, 23, 90, 11};
int size = 8;
// Example 1: Print array
cout << "Original array: ";
printArray(numbers, size);
cout << endl;
// Example 2: Find maximum
cout << "Maximum value: " << findMax(numbers, size) << endl;
// Example 3: Calculate sum
cout << "Sum of elements: " << findSum(numbers, size) << endl;
// Example 4: Reverse array
cout << "\nReversing array..." << endl;
reverseArray(numbers, size);
cout << "Reversed array: ";
printArray(numbers, size);
// Reverse back to original
reverseArray(numbers, size);
// Example 5: Sort array
cout << "\nSorting array..." << endl;
sortArray(numbers, size);
cout << "Sorted array: ";
printArray(numbers, size);
cout << endl << "=================================" << endl << endl;
// 2D ARRAY EXAMPLES
cout << "=== 2D ARRAY (MATRIX) OPERATIONS ===" << endl;
const int ROWS = 3;
int matrix[ROWS][COLS] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Example 6: Print 2D array
cout << "\nOriginal matrix:" << endl;
printMatrix(matrix, ROWS);
// Example 7: Sum of all elements
cout << "Sum of all matrix elements: " << sumMatrix(matrix, ROWS) << endl;
// Example 8: Transpose matrix (for square matrix)
cout << "\nSquare matrix for transpose:" << endl;
int squareMatrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// Note: For transpose, we need same rows and columns
cout << "Original square matrix:" << endl;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << setw(4) << squareMatrix[i][j];
}
cout << endl;
}
// Example 9: Dynamic array passing using pointers
cout << "\n=== DYNAMIC ARRAYS ===" << endl;
int* dynamicArray = new int[5];
for (int i = 0; i < 5; i++) {
dynamicArray[i] = (i + 1) * 10;
}
cout << "Dynamic array: ";
printArray(dynamicArray, 5); // Same function works!
delete[] dynamicArray; // Don't forget to free memory
return 0;
}
// 1D ARRAY FUNCTION DEFINITIONS
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
}
int findMax(int arr[], int size) {
int max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
int findSum(int arr[], int size) {
int sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
return sum;
}
void reverseArray(int arr[], int size) {
for (int i = 0; i < size / 2; i++) {
// Swap arr[i] with arr[size-1-i]
int temp = arr[i];
arr[i] = arr[size - 1 - i];
arr[size - 1 - i] = temp;
}
}
void sortArray(int arr[], int size) {
// Simple bubble sort
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// Swap
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 2D ARRAY FUNCTION DEFINITIONS
void printMatrix(int matrix[][COLS], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < COLS; j++) {
cout << setw(4) << matrix[i][j];
}
cout << endl;
}
}
int sumMatrix(int matrix[][COLS], int rows) {
int total = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < COLS; j++) {
total += matrix[i][j];
}
}
return total;
}
- Arrays are always passed by reference (address)
- For 1D arrays: Pass size as separate parameter
- For 2D arrays: Must specify column size in parameter
int arr[]andint *arrare equivalent in parameters- Changes made to array in function affect original
- Use const keyword for read-only array parameters
4. Function Overloading
Function overloading allows multiple functions with the same name but different parameters. The compiler distinguishes them based on parameter count or types.
Overloading Syntax
// 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);
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
// Overloaded function prototypes
int add(int a, int b);
double add(double a, double b);
int add(int a, int b, int c);
string add(string a, string b);
// Overloaded area functions
double area(double radius); // Circle
double area(double length, double width); // Rectangle
double area(double base, double height, char); // Triangle
// Overloaded display functions
void display(int value);
void display(double value);
void display(string value);
void display(int value, int precision);
// Overloaded max functions
int max(int a, int b);
double max(double a, double b);
int max(int a, int b, int c);
int main() {
cout << "=== FUNCTION OVERLOADING DEMONSTRATION ===" << endl << endl;
// Example 1: Overloaded add functions
cout << "=== Example 1: Overloaded Add Functions ===" << endl;
cout << "add(5, 10) = " << add(5, 10) << endl;
cout << "add(2.5, 3.7) = " << add(2.5, 3.7) << endl;
cout << "add(1, 2, 3) = " << add(1, 2, 3) << endl;
cout << "add(\"Hello, \", \"World!\") = " << add("Hello, ", "World!") << endl;
cout << endl;
// Example 2: Overloaded area functions
cout << "=== Example 2: Overloaded Area Functions ===" << endl;
cout << "Area of circle (radius 5.0) = " << area(5.0) << endl;
cout << "Area of rectangle (4.0 x 6.0) = " << area(4.0, 6.0) << endl;
cout << "Area of triangle (base 3.0, height 4.0) = "
<< area(3.0, 4.0, 'T') << endl;
cout << endl;
// Example 3: Overloaded display functions
cout << "=== Example 3: Overloaded Display Functions ===" << endl;
display(42);
display(3.14159);
display("C++ Programming");
display(3.14159, 3);
cout << endl;
// Example 4: Overloaded max functions
cout << "=== Example 4: Overloaded Max Functions ===" << endl;
cout << "max(10, 20) = " << max(10, 20) << endl;
cout << "max(5.5, 2.3) = " << max(5.5, 2.3) << endl;
cout << "max(15, 30, 10) = " << max(15, 30, 10) << endl;
// Example 5: Ambiguity demonstration
cout << "\n=== Example 5: Potential Ambiguity ===" << endl;
// Uncommenting the next line would cause ambiguity error
// display(10); // Which display? int or double?
// Solution: Use explicit cast
display(static_cast<double>(10));
return 0;
}
// Overloaded add function definitions
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;
}
string add(string a, string b) {
return a + b;
}
// Overloaded area function definitions
double area(double radius) {
return 3.14159 * radius * radius;
}
double area(double length, double width) {
return length * width;
}
double area(double base, double height, char) {
return 0.5 * base * height;
}
// Overloaded display function definitions
void display(int value) {
cout << "Integer: " << value << endl;
}
void display(double value) {
cout << "Double: " << value << endl;
}
void display(string value) {
cout << "String: " << value << endl;
}
void display(double value, int precision) {
cout.precision(precision);
cout << "Double with precision " << precision << ": "
<< fixed << value << endl;
}
// Overloaded max function definitions
int max(int a, int b) {
return (a > b) ? a : b;
}
double max(double a, double b) {
return (a > b) ? a : b;
}
int max(int a, int b, int c) {
return max(max(a, b), c);
}
- Overload based on parameter types/count, not return type
- Maintain consistent functionality across overloads
- Avoid ambiguous overloads that confuse compiler
- Use default parameters as alternative to overloading
- Document each overload's purpose clearly
5. Recursive Functions
A recursive function calls itself to solve a problem. It must have a base case (stopping condition) and a recursive case (calls itself).
Recursion Structure
return_type function(parameters) {
// BASE CASE (stopping condition)
if (stopping_condition) {
return base_value;
}
// RECURSIVE CASE (call itself)
return function(modified_parameters);
}
#include <iostream>
using namespace std;
// Recursive function prototypes
int factorial(int n);
int fibonacci(int n);
int power(int base, int exponent);
int sumOfDigits(int n);
int gcd(int a, int b);
void printNumbers(int n);
void towerOfHanoi(int n, char from, char to, char aux);
int binarySearch(int arr[], int left, int right, int target);
int main() {
cout << "=== RECURSIVE FUNCTIONS DEMONSTRATION ===" << endl << endl;
// Example 1: Factorial
cout << "=== Example 1: Factorial ===" << endl;
for (int i = 0; i <= 5; i++) {
cout << i << "! = " << factorial(i) << endl;
}
cout << endl;
// Example 2: Fibonacci sequence
cout << "=== Example 2: Fibonacci Sequence ===" << endl;
cout << "First 10 Fibonacci numbers: ";
for (int i = 0; i < 10; i++) {
cout << fibonacci(i) << " ";
}
cout << endl << endl;
// Example 3: Power calculation
cout << "=== Example 3: Power Calculation ===" << endl;
int base = 2, exponent = 5;
cout << base << "^" << exponent << " = " << power(base, exponent) << endl;
cout << endl;
// Example 4: Sum of digits
cout << "=== Example 4: Sum of Digits ===" << endl;
int number = 12345;
cout << "Sum of digits of " << number << " = " << sumOfDigits(number) << endl;
cout << endl;
// Example 5: Greatest Common Divisor (GCD)
cout << "=== Example 5: GCD Calculation ===" << endl;
int a = 56, b = 98;
cout << "GCD of " << a << " and " << b << " = " << gcd(a, b) << endl;
cout << endl;
// Example 6: Print numbers recursively
cout << "=== Example 6: Print Numbers 1 to 5 ===" << endl;
printNumbers(5);
cout << endl << endl;
// Example 7: Binary Search (recursive)
cout << "=== Example 7: Recursive Binary Search ===" << endl;
int sortedArray[] = {2, 5, 8, 12, 16, 23, 38, 45, 56, 72};
int size = 10;
int target = 23;
int index = binarySearch(sortedArray, 0, size - 1, target);
if (index != -1) {
cout << "Element " << target << " found at index " << index << endl;
} else {
cout << "Element " << target << " not found" << endl;
}
cout << endl;
// Example 8: Tower of Hanoi
cout << "=== Example 8: Tower of Hanoi (3 disks) ===" << endl;
towerOfHanoi(3, 'A', 'C', 'B');
return 0;
}
// Recursive function definitions
// Factorial: n! = n * (n-1)!
int factorial(int n) {
// Base case
if (n <= 1) {
return 1;
}
// Recursive case
return n * factorial(n - 1);
}
// Fibonacci: fib(n) = fib(n-1) + fib(n-2)
int fibonacci(int n) {
// Base cases
if (n == 0) return 0;
if (n == 1) return 1;
// Recursive case
return fibonacci(n - 1) + fibonacci(n - 2);
}
// Power: base^exponent = base * base^(exponent-1)
int power(int base, int exponent) {
// Base case
if (exponent == 0) {
return 1;
}
// Recursive case
return base * power(base, exponent - 1);
}
// Sum of digits: sum(123) = 3 + sum(12)
int sumOfDigits(int n) {
// Base case
if (n == 0) {
return 0;
}
// Recursive case: last digit + sum of remaining digits
return (n % 10) + sumOfDigits(n / 10);
}
// GCD using Euclidean algorithm
int gcd(int a, int b) {
// Base case
if (b == 0) {
return a;
}
// Recursive case
return gcd(b, a % b);
}
// Print numbers from 1 to n
void printNumbers(int n) {
// Base case
if (n <= 0) {
return;
}
// Recursive call first (to print 1 to n-1)
printNumbers(n - 1);
// Then print n
cout << n << " ";
}
// Recursive binary search
int binarySearch(int arr[], int left, int right, int target) {
// Base case: element not found
if (left > right) {
return -1;
}
int mid = left + (right - left) / 2;
// Base case: element found
if (arr[mid] == target) {
return mid;
}
// Recursive cases
if (arr[mid] > target) {
// Search left half
return binarySearch(arr, left, mid - 1, target);
} else {
// Search right half
return binarySearch(arr, mid + 1, right, target);
}
}
// Tower of Hanoi
void towerOfHanoi(int n, char from, char to, char aux) {
// Base case: only one disk
if (n == 1) {
cout << "Move disk 1 from " << from << " to " << to << endl;
return;
}
// Move n-1 disks from 'from' to 'aux' using 'to'
towerOfHanoi(n - 1, from, aux, to);
// Move nth disk from 'from' to 'to'
cout << "Move disk " << n << " from " << from << " to " << to << endl;
// Move n-1 disks from 'aux' to 'to' using 'from'
towerOfHanoi(n - 1, aux, to, from);
}
- Always have a base case to prevent infinite recursion
- Recursion can be inefficient (stack overhead, repeated calculations)
- May cause stack overflow for deep recursion
- Use iteration when possible for better performance
- Consider memoization (caching) for optimization
6. Best Practices and Common Mistakes
Function Best Practices
- One function = One responsibility
- Use descriptive function names
- Keep functions small (< 50 lines)
- Use const references for large read-only parameters
- Document function purpose, parameters, return value
- Validate parameters at function entry
- Use function prototypes for better organization
Common Mistakes
- Forgetting return statement in non-void function
- Mismatched function prototype and definition
- Passing wrong array size to functions
- Infinite recursion without base case
- Global variables instead of parameters
- Functions that are too long and complex
- Not handling edge cases in parameters
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// ===== BAD PRACTICES =====
void processEverything(int data[], int size) { // Too broad responsibility
// 100+ lines of code doing multiple things
// Hard to read, debug, and maintain
}
int calculate(int a, int b) { // Vague name
return a + b; // But what if we want subtraction later?
}
void printData(int arr[], int n) { // No parameter validation
for (int i = 0; i < n; i++) { // What if n is negative or too large?
cout << arr[i] << " ";
}
}
// Global variable abuse
int counter = 0; // Bad: Global variable
void increment() {
counter++; // Function depends on global state
}
// ===== GOOD PRACTICES =====
// Good: Single responsibility
double calculateAverage(const vector<double>& numbers) {
if (numbers.empty()) {
return 0.0; // Handle edge case
}
double sum = 0.0;
for (double num : numbers) {
sum += num;
}
return sum / numbers.size();
}
// Good: Descriptive name, const reference for efficiency
void printStudentInfo(const string& name, int age, double gpa) {
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
cout << "GPA: " << gpa << endl;
}
// Good: Parameter validation
bool isValidIndex(int index, int arraySize) {
return (index >= 0 && index < arraySize);
}
// Good: Small, focused function
int findMaxValue(const int arr[], int size) {
if (size <= 0) return INT_MIN; // Validate input
int maxVal = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > maxVal) {
maxVal = arr[i];
}
}
return maxVal;
}
// Good: Function with clear documentation
/**
* Calculates the factorial of a non-negative integer.
*
* @param n The number to calculate factorial for (must be >= 0)
* @return The factorial of n
* @throws invalid_argument if n is negative
*/
long long factorial(int n) {
if (n < 0) {
throw invalid_argument("Factorial not defined for negative numbers");
}
long long result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
// Good: Function returning multiple values (using references)
bool solveQuadratic(double a, double b, double c,
double& root1, double& root2) {
double discriminant = b * b - 4 * a * c;
if (discriminant < 0) {
return false; // No real roots
}
root1 = (-b + sqrt(discriminant)) / (2 * a);
root2 = (-b - sqrt(discriminant)) / (2 * a);
return true;
}
int main() {
cout << "=== FUNCTION DESIGN COMPARISON ===" << endl << endl;
// Good practice examples
vector<double> scores = {85.5, 92.0, 78.5, 88.0};
cout << "Average score: " << calculateAverage(scores) << endl;
printStudentInfo("Alice Johnson", 21, 3.75);
int numbers[] = {45, 12, 89, 34, 67};
cout << "Maximum value: " << findMaxValue(numbers, 5) << endl;
// Using function with multiple return values
double root1, root2;
if (solveQuadratic(1, -3, 2, root1, root2)) {
cout << "Quadratic roots: " << root1 << ", " << root2 << endl;
}
return 0;
}