Define and Use Functions in C++

C++C++Beginner
Practice Now

Introduction

In this lab, you will delve into the world of functions in C++. Functions are a cornerstone of programming, allowing you to organize your code into manageable, reusable blocks. This lab covers a wide array of function-related topics, from creating functions with various return types to leveraging advanced concepts like recursion and function overloading. You'll learn how to define functions, pass parameters by value and by reference, set default parameter values, and understand function prototypes. Additionally, you'll discover how to return multiple values using structures. By the end of this lab, you'll have a solid foundation for writing modular, efficient, and well-structured C++ code.

We'll begin by exploring functions with different return types, such as integers, floating-point numbers, characters, and strings. You'll then learn the critical difference between passing parameters by value and by reference, and how default parameter values can add flexibility to your functions. We will also cover function overloading, which allows you to define multiple functions with the same name but different parameter lists. Additionally, you will explore recursive functions, a powerful tool for solving problems by calling themselves. You'll see how to use function prototypes in header files to create more organized code. Finally, you'll discover how to return multiple values from a function using structures.

Create Functions with Different Return Types

Functions are a foundational concept in programming. They allow you to break down large programs into smaller, manageable pieces, making your code easier to write, understand, and maintain. Functions can also return values, enabling data to be passed back to the calling part of the program.

Here's the basic syntax of a function in C++:

return_type function_name(parameters) {
    // Function body
    return value;
}
  • return_type: This specifies the data type of the value that the function will return. If the function does not return any value, you use the keyword void.
  • function_name: This is the name that you'll use to call the function. Choose a name that clearly describes the function's purpose.
  • parameters: These are the input values that you pass to the function. They are optional, and you can have zero or more parameters.
  • value: This is the value that the function returns. It should match the return_type of the function. If the return_type is void, you omit the return statement or simply use return; without a value.

In this step, you'll learn to create functions that return different data types. This ability is crucial for building versatile and functional programs.

Open the WebIDE and create a new file called function_types.cpp in the ~/project directory:

touch ~/project/function_types.cpp

Open the function_types.cpp file in the editor and add the following code:

#include <iostream>
#include <string>

// Function returning an integer
int addNumbers(int a, int b) {
    return a + b;
}

// Function returning a double (floating-point number)
double calculateAverage(double x, double y) {
    return (x + y) / 2.0;
}

// Function returning a character
char 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';
}

// Function returning a string
std::string getStudentStatus(bool isEnrolled) {
    return isEnrolled ? "Active" : "Inactive";
}

int main() {
    // Demonstrating different return types
    int sum = addNumbers(5, 3);
    std::cout << "Sum: " << sum << std::endl;

    double average = calculateAverage(10.5, 20.5);
    std::cout << "Average: " << average << std::endl;

    char grade = getGrade(85);
    std::cout << "Grade: " << grade << std::endl;

    std::string status = getStudentStatus(true);
    std::cout << "Student Status: " << status << std::endl;

    return 0;
}

Compile and run the program:

g++ function_types.cpp -o function_types
./function_types

Example output:

Sum: 8
Average: 15.5
Grade: B
Student Status: Active

Key points about function return types:

  • Functions can return different data types, providing flexibility to the code.
  • The return_type is specified before the function name, indicating what kind of data the function will send back.
  • The return statement must match the declared return_type or the compiler will report an error.
  • void functions do not return a value, and you use return; or simply omit the return statement if you are at the end of a void function.
  • You can return primitive types (like int, double, char), strings, and even user-defined types.
  • Think of function return types as different types of containers. Just as you'd use a box, a bag, or a basket to carry different items, functions use different return types to send back different kinds of data.

Pass Parameters to Functions by Value

In this step, you'll learn about passing parameters to functions by value in C++. When you pass a parameter by value, a copy of the original argument is made, and that copy is used within the function. Any changes made to the parameter inside the function do not affect the original variable outside of the function.

Open the WebIDE and create a new file called pass_by_value.cpp in the ~/project directory:

touch ~/project/pass_by_value.cpp

Open the pass_by_value.cpp file in the editor and add the following code:

#include <iostream>

// Function that demonstrates pass by value
void modifyValue(int x) {
    // This modification only affects the local copy, not the original variable
    x = x * 2;
    std::cout << "Inside function - Value of x: " << x << std::endl;
}

int main() {
    // Original variable
    int number = 10;

    // Print original value
    std::cout << "Before function call - Original value: " << number << std::endl;

    // Call the function with the original variable
    modifyValue(number);

    // Original variable remains unchanged
    std::cout << "After function call - Original value: " << number << std::endl;

    return 0;
}

Compile and run the program:

g++ pass_by_value.cpp -o pass_by_value
./pass_by_value

Example output:

Before function call - Original value: 10
Inside function - Value of x: 20
After function call - Original value: 10

Let's create another example to further illustrate pass by value:

touch ~/project/swap_values.cpp

Add the following code to swap_values.cpp:

#include <iostream>
#include <string>

// Function to swap values (will not work as expected with pass by value)
void swapValues(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    std::cout << "Inside function - a: " << a << ", b: " << b << std::endl;
}

int main() {
    int first = 5;
    int second = 10;

    std::cout << "Before swap - first: " << first << ", second: " << second << std::endl;

    // Call swap function
    swapValues(first, second);

    // Original variables remain unchanged
    std::cout << "After swap - first: " << first << ", second: " << second << std::endl;

    return 0;
}

Compile and run the second program:

g++ swap_values.cpp -o swap_values
./swap_values

Example output:

Before swap - first: 5, second: 10
Inside function - a: 10, b: 5
After swap - first: 5, second: 10

Key points about pass by value:

  • A copy of the argument is passed to the function, preserving the original value.
  • Changes made to the parameter inside the function do not affect the original variable outside the function.
  • Pass by value is suitable for simple data types like int, char, float etc, when you don't need to modify the original value.
  • When passing large objects by value, copying can consume more memory and time, making it less efficient compared to passing by reference.

You can think of pass by value like making a photocopy of a document. The original remains unchanged, and you can modify the copy without affecting the original.

Implement Pass by Reference Using & Operator

In this step, you'll learn how to pass parameters to functions by reference using the & operator in C++. Pass by reference allows you to provide the function with direct access to the original variable, meaning any changes made to the parameter within the function will directly modify the original variable outside the function.

Open the WebIDE and create a new file called pass_by_reference.cpp in the ~/project directory:

touch ~/project/pass_by_reference.cpp

Open the pass_by_reference.cpp file in the editor and add the following code:

#include <iostream>

// Function that demonstrates pass by reference
void swapValues(int& a, int& b) {
    // Directly modify the original variables
    int temp = a;
    a = b;
    b = temp;
}

// Function to modify a value using reference
void incrementValue(int& x) {
    // Directly increases the original variable
    x++;
}

int main() {
    // Swap example
    int first = 5;
    int second = 10;

    std::cout << "Before swap - first: " << first << ", second: " << second << std::endl;

    // Call swap function with references
    swapValues(first, second);

    std::cout << "After swap - first: " << first << ", second: " << second << std::endl;

    // Increment example
    int number = 7;

    std::cout << "Before increment: " << number << std::endl;

    // Call increment function with reference
    incrementValue(number);

    std::cout << "After increment: " << number << std::endl;

    return 0;
}

Compile and run the program:

g++ pass_by_reference.cpp -o pass_by_reference
./pass_by_reference

Example output:

Before swap - first: 5, second: 10
After swap - first: 10, second: 5
Before increment: 7
After increment: 8

Let's create another example to demonstrate the power of pass by reference:

touch ~/project/string_reference.cpp

Add the following code to string_reference.cpp:

#include <iostream>
#include <string>

// Function to modify a string using reference
void appendText(std::string& text) {
    text += " - Modified";
}

int main() {
    std::string message = "Hello, World!";

    std::cout << "Original message: " << message << std::endl;

    // Modify string directly using reference
    appendText(message);

    std::cout << "Modified message: " << message << std::endl;

    return 0;
}

Compile and run the second program:

g++ string_reference.cpp -o string_reference
./string_reference

Example output:

Original message: Hello, World!
Modified message: Hello, World! - Modified

Key points about pass by reference:

  • Use & after the type to create a reference parameter (e.g., int& a).
  • Directly modifies the original variable, unlike pass by value which uses a copy.
  • Avoids the overhead of copying large objects, making it more efficient for complex data types.
  • Useful when you need to modify the original value or work with complex data types without copying.

You can think of pass by reference like giving someone the original document instead of a photocopy. Any changes they make will directly affect the original.

Set Default Values for Function Parameters

In this step, you'll learn how to set default values for function parameters in C++. Default parameters allow you to assign default values to function arguments, making your functions more versatile and easier to use. When a caller omits a parameter with a default value, the default value is automatically used.

Open the WebIDE and create a new file called default_parameters.cpp in the ~/project directory:

touch ~/project/default_parameters.cpp

Open the default_parameters.cpp file in the editor and add the following code:

#include <iostream>
#include <string>

// Function with default parameters
void greetUser(std::string name = "Guest", int age = 0) {
    std::cout << "Hello, " << name << "!" << std::endl;

    if (age > 0) {
        std::cout << "You are " << age << " years old." << std::endl;
    }
}

// Another example with default parameters for calculation
double calculateArea(double length = 1.0, double width = 1.0) {
    return length * width;
}

int main() {
    // Call function with no arguments (uses default values)
    greetUser();

    // Call function with partial arguments
    greetUser("Alice");

    // Call function with all arguments
    greetUser("Bob", 25);

    // Demonstrate area calculation with default parameters
    std::cout << "\nArea calculations:" << std::endl;

    // Default area (1x1)
    std::cout << "Default area: " << calculateArea() << std::endl;

    // Area with one parameter
    std::cout << "Area with length 5: " << calculateArea(5.0) << std::endl;

    // Area with both parameters
    std::cout << "Area with length 5 and width 3: " << calculateArea(5.0, 3.0) << std::endl;

    return 0;
}

Compile and run the program:

g++ default_parameters.cpp -o default_parameters
./default_parameters

Example output:

Hello, Guest!
Hello, Alice!
Hello, Bob!
You are 25 years old.

Area calculations:
Default area: 1
Area with length 5: 5
Area with length 5 and width 3: 15

Key points about default parameters:

  • Default values are specified in the function declaration, right after the parameter type (e.g., int age = 0).
  • Parameters with default values must be at the end of the parameter list. You cannot have non-default parameters after default parameters.
  • You can call the function with fewer arguments, and the missing ones will use their default values.
  • Default values provide flexibility and reduce the need for multiple function overloads that do the same thing with slightly different inputs.

Let's create another example to demonstrate more complex default parameters:

touch ~/project/student_info.cpp

Add the following code to student_info.cpp:

#include <iostream>
#include <string>

// Function with multiple default parameters
void printStudentInfo(
    std::string name = "Unknown",
    int age = 0,
    std::string grade = "N/A",
    bool isEnrolled = false
) {
    std::cout << "Name: " << name << std::endl;
    std::cout << "Age: " << age << std::endl;
    std::cout << "Grade: " << grade << std::endl;
    std::cout << "Enrollment Status: " << (isEnrolled ? "Enrolled" : "Not Enrolled") << std::endl;
}

int main() {
    // Different ways to call the function
    printStudentInfo();  // All defaults
    std::cout << "\n";
    printStudentInfo("John");  // Only name
    std::cout << "\n";
    printStudentInfo("Alice", 20);  // Name and age
    std::cout << "\n";
    printStudentInfo("Bob", 22, "A", true);  // All parameters

    return 0;
}

Compile and run the second program:

g++ student_info.cpp -o student_info
./student_info

Example output:

Name: Unknown
Age: 0
Grade: N/A
Enrollment Status: Not Enrolled

Name: John
Age: 0
Grade: N/A
Enrollment Status: Not Enrolled

Name: Alice
Age: 20
Grade: N/A
Enrollment Status: Not Enrolled

Name: Bob
Age: 22
Grade: A
Enrollment Status: Enrolled

You can think of default parameters like a buffet where you can choose to take all items or just a few, with some items automatically filled in if you don't specify them.

Create Function Overloads with Different Parameters

In this step, you'll learn about function overloading in C++, a feature that allows you to create multiple functions with the same name, as long as they have different parameter lists (either in the number of parameters or the types of parameters). This feature makes your code more flexible and readable by allowing you to perform similar operations on different data types or with a varying number of inputs using the same function name.

Open the WebIDE and create a new file called function_overloads.cpp in the ~/project directory:

touch ~/project/function_overloads.cpp

Open the function_overloads.cpp file in the editor and add the following code:

#include <iostream>
#include <string>

// Function overloads for adding different types of numbers
int add(int a, int b) {
    std::cout << "Adding two integers" << std::endl;
    return a + b;
}

double add(double a, double b) {
    std::cout << "Adding two doubles" << std::endl;
    return a + b;
}

// Function overloads with different number of parameters
int add(int a, int b, int c) {
    std::cout << "Adding three integers" << std::endl;
    return a + b + c;
}

// Function overload with different parameter types
std::string add(std::string a, std::string b) {
    std::cout << "Concatenating strings" << std::endl;
    return a + b;
}

int main() {
    // Calling different overloaded functions
    std::cout << "Integer addition: " << add(5, 3) << std::endl;
    std::cout << "Double addition: " << add(5.5, 3.7) << std::endl;
    std::cout << "Three integer addition: " << add(1, 2, 3) << std::endl;
    std::cout << "String concatenation: " << add("Hello, ", "World!") << std::endl;

    return 0;
}

Compile and run the program:

g++ function_overloads.cpp -o function_overloads
./function_overloads

Example output:

Adding two integers
Integer addition: 8
Adding two doubles
Double addition: 9.2
Adding three integers
Three integer addition: 6
Concatenating strings
String concatenation: Hello, World!

Let's create another example to demonstrate more function overloading:

touch ~/project/overloading_examples.cpp

Add the following code to overloading_examples.cpp:

#include <iostream>
#include <string>

// Overloaded print functions
void print(int value) {
    std::cout << "Printing integer: " << value << std::endl;
}

void print(double value) {
    std::cout << "Printing double: " << value << std::endl;
}

void print(std::string value) {
    std::cout << "Printing string: " << value << std::endl;
}

// Overloaded calculate function
int calculate(int a, int b) {
    return a + b;
}

double calculate(double a, double b) {
    return a * b;
}

int main() {
    // Demonstrating function overloading
    print(42);
    print(3.14);
    print("Hello, Overloading!");

    std::cout << "Integer calculation: " << calculate(5, 3) << std::endl;
    std::cout << "Double calculation: " << calculate(5.5, 3.2) << std::endl;

    return 0;
}

Compile and run the second program:

g++ overloading_examples.cpp -o overloading_examples
./overloading_examples

Example output:

Printing integer: 42
Printing double: 3.14
Printing string: Hello, Overloading!
Integer calculation: 8
Double calculation: 17.6

Key points about function overloading:

  • Functions can have the same name as long as they have different parameter lists (different types or different numbers of parameters).
  • The C++ compiler decides which overloaded function to call based on the types and number of arguments provided during the function call.
  • Function overloading leads to more readable and intuitive code, allowing you to reuse names for similar actions.
  • You cannot overload functions if they differ only by their return types. The parameters must be different for overloading to work correctly.

You can think of function overloading like a universal remote that works with different types of devices. The same button (function name) does different things depending on the context (the arguments you provide).

Write Recursive Functions for Factorial Calculation

In this step, you'll learn about recursive functions by implementing a factorial calculation program. Recursion is a programming technique where a function calls itself to solve a problem. To avoid infinite loops, recursive functions must have a base case where they stop the recursive call, along with recursive case to keep calling itself to work toward the base case.

Open the WebIDE and create a new file called factorial_recursive.cpp in the ~/project directory:

touch ~/project/factorial_recursive.cpp

Open the factorial_recursive.cpp file in the editor and add the following code:

#include <iostream>

// Recursive function to calculate factorial
unsigned long long calculateFactorial(int n) {
    // Base case: factorial of 0 or 1 is 1
    if (n == 0 || n == 1) {
        return 1;
    }

    // Recursive case: n! = n * (n-1)!
    return n * calculateFactorial(n - 1);
}

int main() {
    // Calculate factorial for different numbers
    int numbers[] = {0, 1, 5, 7, 10};

    for (int num : numbers) {
        unsigned long long result = calculateFactorial(num);
        std::cout << "Factorial of " << num << " is: " << result << std::endl;
    }

    return 0;
}

Compile and run the program:

g++ factorial_recursive.cpp -o factorial_recursive
./factorial_recursive

Example output:

Factorial of 0 is: 1
Factorial of 1 is: 1
Factorial of 5 is: 120
Factorial of 7 is: 5040
Factorial of 10 is: 3628800

Let's create another example with a more detailed recursive function:

touch ~/project/factorial_steps.cpp

Add the following code to factorial_steps.cpp:

#include <iostream>

// Recursive function with step-by-step output
unsigned long long calculateFactorialWithSteps(int n, int depth = 0) {
    // Add indentation for visualization
    std::string indent(depth * 2, ' ');

    // Base case: factorial of 0 or 1 is 1
    if (n == 0 || n == 1) {
        std::cout << indent << "Base case: factorial(" << n << ") = 1" << std::endl;
        return 1;
    }

    // Recursive case with visualization
    std::cout << indent << "Calculating factorial(" << n << ")" << std::endl;

    // Recursive call
    unsigned long long subResult = calculateFactorialWithSteps(n - 1, depth + 1);

    // Combine results
    unsigned long long result = n * subResult;

    std::cout << indent << "factorial(" << n << ") = "
              << n << " * factorial(" << n-1 << ") = "
              << result << std::endl;

    return result;
}

int main() {
    int number = 5;

    std::cout << "Factorial calculation steps for " << number << ":" << std::endl;
    unsigned long long result = calculateFactorialWithSteps(number);

    std::cout << "\nFinal result: " << number << "! = " << result << std::endl;

    return 0;
}

Compile and run the second program:

g++ factorial_steps.cpp -o factorial_steps
./factorial_steps

Example output:

Factorial calculation steps for 5:
Calculating factorial(5)
  Calculating factorial(4)
    Calculating factorial(3)
      Calculating factorial(2)
        Calculating factorial(1)
        Base case: factorial(1) = 1
      factorial(2) = 2 * factorial(1) = 2
    factorial(3) = 3 * factorial(2) = 6
  factorial(4) = 4 * factorial(3) = 24
factorial(5) = 5 * factorial(4) = 120

Final result: 5! = 120

Key points about recursive functions:

  • Recursive functions call themselves: A function calls itself to solve smaller subproblems of the same type.
  • Base Case: A recursive function must have a base case, a condition that stops the recursive calls. Without a base case, the function will call itself infinitely, leading to a stack overflow error.
  • Recursive Case: The recursive case is where the function calls itself to move towards the base case.
  • Problem Decomposition: Recursion breaks down a complex problem into smaller, simpler subproblems, making it easier to manage.
  • Stack usage: Each recursive call takes up space on the program's call stack. Deep recursion can lead to stack overflow, so be aware of the call stack limits.
  • Suitable Problems: Recursion is particularly well-suited for problems that can be naturally defined in terms of smaller, similar subproblems, such as tree traversals, divide-and-conquer algorithms, and fractal generation.

You can think of recursion like a set of nested Russian dolls, where each doll contains a smaller version of itself, and the innermost doll represents the base case that stops the nesting process.

Use Function Prototypes in Header Files

In this step, you'll learn about function prototypes and how to use header files to organize and declare functions in C++. Function prototypes declare the function's name, return type, and parameters without providing the implementation (the body of the function). By using header files, you separate the interface of a function (its declaration) from its implementation. This separation improves code organization, making it easier to maintain and reuse functions in multiple parts of your program or in multiple programs.

Open the WebIDE and create three files in the ~/project directory:

First, create math_functions.h:

touch ~/project/math_functions.h

Add the following code to math_functions.h:

#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H

// Function prototypes for mathematical operations
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
double divide(double a, double b);

#endif // MATH_FUNCTIONS_H

.h files are used for declarations, containing function prototypes and other declarations but not implementations. This way, you can declare functions without implementing them. The #ifndef, #define, and #endif directives are called include guards, and they prevent multiple inclusions of the header file, which can cause errors.

Next, create math_functions.cpp:

touch ~/project/math_functions.cpp

Add the following code to math_functions.cpp:

#include "math_functions.h"

// Function implementations
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(double a, double b) {
    // Check for division by zero
    if (b == 0) {
        return 0;
    }
    return a / b;
}

This .cpp file contains the actual implementations of the functions that were declared in the header file.

Finally, create main.cpp:

touch ~/project/main.cpp

Add the following code to main.cpp:

#include <iostream>
#include "math_functions.h"

int main() {
    // Demonstrate function calls from header file
    int x = 10, y = 5;

    std::cout << "Addition: " << x << " + " << y << " = " << add(x, y) << std::endl;
    std::cout << "Subtraction: " << x << " - " << y << " = " << subtract(x, y) << std::endl;
    std::cout << "Multiplication: " << x << " * " << y << " = " << multiply(x, y) << std::endl;
    std::cout << "Division: " << x << " / " << y << " = " << divide(x, y) << std::endl;

    return 0;
}

This main.cpp file includes the math_functions.h header file, which makes the function prototypes available. It can then use the implemented functions in math_functions.cpp.

Compile the program using multiple source files:

g++ math_functions.cpp main.cpp -o math_operations
./math_operations

Example output:

Addition: 10 + 5 = 15
Subtraction: 10 - 5 = 5
Multiplication: 10 * 5 = 50
Division: 10 / 5 = 2

Let's create another example with a more complex header file:

Create calculator.h:

touch ~/project/calculator.h

Add the following code to calculator.h:

#ifndef CALCULATOR_H
#define CALCULATOR_H

class Calculator {
public:
    // Function prototypes for calculator operations
    int add(int a, int b);
    int subtract(int a, int b);
    int calculate(int a, int b, char operation);
};

#endif // CALCULATOR_H

This header file declares a class called Calculator and its public methods.

Create calculator.cpp:

touch ~/project/calculator.cpp

Add the following code to calculator.cpp:

#include "calculator.h"

int Calculator::add(int a, int b) {
    return a + b;
}

int Calculator::subtract(int a, int b) {
    return a - b;
}

int Calculator::calculate(int a, int b, char operation) {
    switch (operation) {
        case '+': return add(a, b);
        case '-': return subtract(a, b);
        default: return 0;
    }
}

This calculator.cpp provides the implementations for the methods declared in the calculator.h header file.

Create calculator_main.cpp:

touch ~/project/calculator_main.cpp

Add the following code to calculator_main.cpp:

#include <iostream>
#include "calculator.h"

int main() {
    Calculator calc;

    std::cout << "Calculator Operations:" << std::endl;
    std::cout << "10 + 5 = " << calc.calculate(10, 5, '+') << std::endl;
    std::cout << "10 - 5 = " << calc.calculate(10, 5, '-') << std::endl;

    return 0;
}

This main file uses the Calculator class and performs operations.

Compile the calculator program:

g++ calculator.cpp calculator_main.cpp -o calculator
./calculator

Example output:

Calculator Operations:
10 + 5 = 15
10 - 5 = 5

Key points about function prototypes and header files:

  • Header files (.h) declare function prototypes, classes, and other declarations. They act as an interface, outlining what functions are available.
  • Source files (.cpp) implement the actual functions declared in the header files. They contain the code for how the functions work.
  • #ifndef, #define, and #endif (include guards) prevent multiple inclusions of the same header file, which avoids potential compilation errors.
  • Using header files promotes modularity and code reusability.
  • Header files allow you to separate the "what" (declarations) from the "how" (implementations).
  • They facilitate code organization, making it easier to maintain and understand.

You can think of header files like a restaurant menu. The menu (header) lists what's available, while the kitchen (source file) prepares the actual dishes.

Return Multiple Values Using Structures

In this step, you'll learn how to use structures to return multiple values from a function in C++. Structures are user-defined data types that allow you to group different types of data together under a single name. They are ideal for returning multiple related values from a function in a structured and organized way.

Open the WebIDE and update the student_info.cpp file in the ~/project directory:

Replace the existing code with the following:

#include <iostream>
#include <string>

// Define a structure to hold student information
struct StudentResult {
    std::string name;
    int score;
    bool passed;
};

// Function that returns multiple values using a structure
StudentResult evaluateStudent(std::string name, int testScore) {
    StudentResult result;
    result.name = name;
    result.score = testScore;
    result.passed = (testScore >= 60);

    return result;
}

// Function to calculate multiple statistical values
struct MathStats {
    int sum;
    double average;
    int minimum;
    int maximum;
};

MathStats calculateArrayStats(int numbers[], int size) {
    MathStats stats;
    stats.sum = 0;
    stats.minimum = numbers[0];
    stats.maximum = numbers[0];

    for (int i = 0; i < size; i++) {
        stats.sum += numbers[i];

        if (numbers[i] < stats.minimum) {
            stats.minimum = numbers[i];
        }

        if (numbers[i] > stats.maximum) {
            stats.maximum = numbers[i];
        }
    }

    stats.average = static_cast<double>(stats.sum) / size;

    return stats;
}

int main() {
    // Demonstrate student evaluation
    StudentResult student1 = evaluateStudent("Alice", 75);
    StudentResult student2 = evaluateStudent("Bob", 45);

    std::cout << "Student Results:" << std::endl;
    std::cout << student1.name << " - Score: " << student1.score
              << ", Passed: " << (student1.passed ? "Yes" : "No") << std::endl;
    std::cout << student2.name << " - Score: " << student2.score
              << ", Passed: " << (student2.passed ? "Yes" : "No") << std::endl;

    // Demonstrate array statistics
    int numbers[] = {5, 2, 9, 1, 7};
    int size = sizeof(numbers) / sizeof(numbers[0]);

    MathStats stats = calculateArrayStats(numbers, size);

    std::cout << "\nArray Statistics:" << std::endl;
    std::cout << "Sum: " << stats.sum << std::endl;
    std::cout << "Average: " << stats.average << std::endl;
    std::cout << "Minimum: " << stats.minimum << std::endl;
    std::cout << "Maximum: " << stats.maximum << std::endl;

    return 0;
}

Compile and run the program:

g++ student_info.cpp -o student_info
./student_info

Example output:

Student Results:
Alice - Score: 75, Passed: Yes
Bob - Score: 45, Passed: No

Array Statistics:
Sum: 24
Average: 4.8
Minimum: 1
Maximum: 9

Key points about structures and returning multiple values:

  • Structures group related data together: You can group together variables of different types (e.g., string, int, bool) into a single unit, which can be beneficial when you have related information.
  • Returning complex data types: Using structures, you can return complex data types from functions, making it easy to manage and pass around related sets of data.
  • Organized return values: Returning structures helps in organizing multiple related return values, making the code cleaner and more maintainable.
  • Provides a clear way to pass structured information: Structures provide a clear, named container for returning multiple related values, improving code readability and understandability.

You can think of structures like a multi-compartment lunchbox that can hold different types of food in separate sections.

Summary

In this lab, you've gained a comprehensive understanding of how to define and use functions in C++. You learned to create functions with various return types, pass parameters by value and by reference, set default parameter values, and utilize function overloading. Additionally, you explored recursive functions, how to use function prototypes in header files, and how to return multiple values using structures.

The key concepts covered include:

  • Function Return Types: You learned that functions can return different data types, and the return statement must match the declared return type.
  • Pass by Value and Pass by Reference: You understood the difference between passing parameters by value (making a copy) and by reference (using the original variable).
  • Default Parameters: You learned how to make functions more versatile by assigning default values to function parameters.
  • Function Overloading: You saw how to define multiple functions with the same name but different parameter lists, making code more readable and intuitive.
  • Recursive Functions: You explored the power of recursive functions, where a function calls itself to solve smaller subproblems, along with the importance of base cases to prevent infinite recursion.
  • Function Prototypes and Header Files: You learned how to use header files to organize function declarations, promoting modularity and code reusability.
  • Returning Multiple Values with Structures: You discovered how to return multiple related values from a function using structures, which provide an organized container for data.

By mastering these concepts, you're now well-equipped to write more modular, efficient, and well-structured C++ code, and you have a solid foundation for exploring more advanced programming techniques.

Other C++ Tutorials you may like