"

5 More on Functions

Hussam Ghunaim, Ph.D.

Learning Objectives

At the end of reading this chapter, you should be able to:

  • Write functions that pass arguments by value and by reference.
  • Correctly identify and utilize local and global variables.
  • Write functions that call other functions.
  • Overload functions.
  • Write functions that use default arguments.
  • Correctly use the scope resolution operator :: to access global variables.

Call by Value vs. Call by Reference

In the functions that you have written so far, you have used a technique called passing arguments by value. Which simply means, the arguments are merely copies of the original values. The following example explains this principle further.

Example 01

Enter and run the given code. Then try to explain the outputs.

In the main function, the variable x is declared and initialized to the value of 5 and printed out on the screen. When the function increment is called, the argument x is passed and incremented by 1 inside the function, and printed out the new value of the x variable which now becomes 6. When the function increment terminates and the execution continues back to line 13,  the variable x is printed again, showing the old value of x, NOT the incremented one. This result proves that the variable x incremented inside the function increment is different from the variable x declared in the main function. To explain this, we can say that when increment (x) is called, a copy of the original variable is made and passed as an argument to the function increment.

ch05_01

If this behavior does not satisfy our needs, we can use another technique called passing arguments by reference. As the name suggests, we are not passing the actual variable value; instead, we are passing the reference to it, in other words, “its address”. In this case, whatever changes happen inside the increment function will be applied to the actual variable and can be seen in both the increment and main functions. The symbol ampersand & is used to pass arguments by reference. The code in the previous example will be entirely the same with only one little difference in the increment function header as follows, void increment(int &x) . Note that the symbol ampersand & can be placed anywhere between the argument name and its type.

ch05_fig01
Figure 01: Passing arguments by reference.

Now, change the code in example 01 to pass the increment function argument by reference and examine the outcomes. When the code runs, you will notice that the incremented value of x is printed from both functions, increment and main. Additionally, you can pass some arguments to the same function by value and the other arguments by reference.

Exercise01

The given code swaps two numbers. Run the code and then answer the questions. Did the code swap the numbers as expected? Explain why? Now, fix the problem. The solution is provided at the end of the chapter.

ch05_exe01

Exercise02

Write a void sortAscending function that accepts two integer arguments passed by reference. The function must check if the two passed numbers are sorted correctly; otherwise, the numbers must be swapped. The program should print these messages:

Before sorting:  x =      , y =
After sorting:  x =      , y =

Scope, Local, and Global Variables

The scope can simply be explained as the variables’ visibility. In other words, when a variable is declared, we need to know where in the code we can use that variable. In example 01, the variable x was declared in the main function on line 10. However, when the variable x was passed to the increment function as an argument, the argument x becomes a new variable that is visible only within the increment function boundaries. Similarly, in the main, the variable x is visible only within the main function. Here we can realize the power of scoping as a way to create variables with the same names and yet without causing any conflicts.

Scope boundaries can generally be determined by the block in which they were created. In other words, block scope can be defined by using { } symbols. Any variable declared in a block is called a local variable in that particular block. If you try to call that variable from another block, an error message will pop up! In some cases, we might need a variable to be accessed from different locations in the code. To do that, the variable needs to be declared as a global variable.

Example02

In the given code, x is declared outside of all code blocks. Hence, it is referred to as a global variable that can be accessed from anywhere. Run the code and examine the outputs to check your understanding.

ch05_exa02

Generally, writing global variables is not recommended as they can make the code harder to understand and debug. To elaborate, the following example uses both global variables and global constants. We can declare a constant by using the keyword const in front of the constant type. Once constants are declared and initialized, any attempt to change their values will cause an error!

Example03

In the given code, one global variable and one global constant are declared and initialized. The first two lines in the main function print their values. When func is called, the first line would return an error if it is executed because it attempts to change the constant value. However, changing the global variable value is possible. Now it is clear what problem this change can cause. Imagine that you created a function and called the global variable. At this point, there is no guarantee of what value the global variable would have because any other function in the code can change its value. In developing large systems, this can be confusing to programmers and generally causes errors.

ch05_exa03

Exercise03

Write a program that declares one global variable and one local variable for both the main function and the printVar function. All variables must have the same name but different values. The program should print the values of all variables.

NOTE: When a local variable has the same name as a global variable, calling that particular variable will by default access the local variable value. To enforce accessing the global variable value instead, we can use the scope operator :: to do that.

::variableName;  // This code will access the global variable named variableName.

Exercise04

As scopes can be defined using the block braces {  }, nesting these blocks will result in nesting scopes. Write a program that demonstrates the concept of nested scopes.  That is, define two variables with the same name but different values in two nested blocks. Print and compare the variables’ values.

Function calls another function

You have already practiced calling functions from within other functions. When you first introduced the predefined functions, these functions were called from within the main function. This technique becomes powerful as the problem we are trying to solve becomes larger and larger, and the solution becomes more complex. Let’s here practice using this concept a little more.

Example 04

Run the given code that calculates the average of a set of numbers entered by a user. The average function calculates the average by dividing two of its local variables, total and counter. The counter variable is needed to count how many numbers are entered by a user. To keep the average function as simple as possible, the code responsible for getting numbers from the user is moved to another function called input. In this case, the average function calls the input function to do its job. The input function has a while loop that keeps asking the user if they still wish to enter more numbers until they answer ‘n’. Note that the input function is void and does not return any variables. Instead, the input function passes its arguments by reference. Although total and counter are local variables to the average function and hence, they can not be called by any other function besides average, the input function has access to them through their references, that is, their addresses. Lastly, the average function returns the result to its caller, the main function.

ch05_exa04

In the above example, instead of keep asking the user whether they want to enter more numbers, we can use another trick that is useful only in some specific cases. In our case, we want to calculate the average of a set of numbers and do not expect users will ever need to input negative numbers, although we understand this is not always the case. Therefore, if we agree that it is safe to make this assumption, we can rewrite the input function to keep looping until a negative number is entered. Below is a new version of the input function that you can use in example 04.

ch05_fig02
Figure 02: A new version of the input function.

Exercises05

Write a program that calculates the area of a rectangle. The program should have three functions: getLength, getWidth, and calculateArea. The getLength and getWidth functions should ask the user for the length and width of the rectangle, respectively, and return their values. The calculateArea function should get the length and width values by calling their respective functions, calculating the area, and printing the result. The main function should call only the calculateArea function.

Exercise06

Write a program that calculates the power of a number. The program should have two functions: getNumber(string prompt), and calculatePower(double base, int exponent). The getNumer function takes a string argument that labels the number entered by the user, whether it is the base or the exponent, and returns the entered number as a double. The calculatePower function should take the base and exponent as parameters, calculate the power based on the given algorithm below, and print the result. The main function should call these functions to perform the task.

Algorithm: To find the power of a number, we multiply a number by itself a certain number of times. This is often written as baseexponent.

  • The base is the number that is being multiplied.
  • The exponent tells us how many times to multiply the base by itself.

For example, if we want to calculate 23, we multiply 2 by itself 3 times: 2 X 2 X 2, which equals 8.

Functions Overloading

Sometimes it is useful to use the same function name several times in the same program. We know this is not possible and returns errors if we try to do it. However, as far as the compiler is concerned, all it wants is a way to distinguish between functions’ calls. To do that, we can use a different number of parameters or types to make the distinction. This technique is called overloading. All the following functions can legally be used in the same program.

func()
func(type1 parameter1)
func(type1 parameter1, type2 parameter2)

Note that we can not overload functions based on their return type. The reason is that when a function is called, its arguments will be specified with the call but not the return type. Therefore, there is no way for the compiler to tell what the return type is until run time.

Example05

Run the given code and notice that the function display is overloaded three times.  The first and second functions are overloaded by providing different argument types. The third function is overloaded by providing a different number of arguments.

ch05_exa05

Example06

This example shows functions that can be overloaded by changing the order of the arguments.

ch05_exa06

Exercise07

Write two overloaded calculateArea functions. One should calculate the area of a rectangle, and the other should calculate the area of a circle.

Exercise08

Write two overloaded calculate functions. The first version of the calculate function should take two integers as parameters and return their sum. The second version of the calculate function should take two integers and a char as parameters. If the char is ‘*’, the function should return the product of the two integers. Otherwise, it should print “Invalid operation. Defaulting to addition” and return the sum of the two integers.

Default Arguments

In writing functions, sometimes it is useful to provide users with different ways to call a function without the need to overload it. This can reduce the size of the overall written code and increase flexibility.

Example07

Run the given code and explain the outputs before continuing reading. The calculateVolume function is written with three parameters, each of which has a default value assigned. Of course, the type of the default value must match the type of the parameter. In the main function, calculateVolume is called in four different ways without overloading. In the first call, no arguments were provided by the user; hence, the compiler is forced to use all available default values. In the second call, only one argument was provided; hence, the compiler will use only the last two available default values. By examining the other calls, it’s important to note that the arguments provided by users will override the default values. This process happens in order. This means that there is no way for users to skip the first or second argument and provide values only for the last argument.ch05_exa07

Example08

When you run the provided code, you will notice that two welcome messages were printed. One greets ‘Guest’ and the other one greets ‘Alice’.

ch05_exa08

Exercise09

Write printDate function that takes three int parameters: day, month, and year. Provide default values for these parameters. In the main function, call printDate function in four different ways, similar to example 7.

Exercise10

Write calculatePower function that takes two parameters: a double for the base and an int for the exponent. Provide a default value only for the exponent parameter. In the main function, call the calculatePower function twice. In the first call, provide only one argument, and in the second call, provide two arguments. Explain the results.

Question: What will happen if you try to call the calculatePower function without providing any arguments? Why?

Note: If a function definition has both parameters with default values and other parameters without default values, the parameters with default values must always be placed last.

Using the scope :: operator

Because the std namespace is really huge, using the entire namespace might result in conflicting names, especially in large programs. Alternatively, it is considered good coding practice to use the scope operator :: to identify only the needed names.

Example 09

We will rewrite example 08 code without the statement “using namespace std;”. Notice how the scope operator is used to identify every name, like the string type and the output stream cout. However, user-defined names do not need to be identified like variables and functions’ names.

ch05_exa09

Example 10

Run the given code and notice the places where the scope operator is used.

ch05_exa10

Exercise11

Write a function named check. This function should take an integer as an argument and return a string. If the integer is even, the function should return ‘even’, otherwise, it should return ‘odd’. In the main function, prompt the user to enter an integer and call the check function with this integer passed as an argument. Finally, print out a message in the following format: The entered number [number] is an [even/odd] number. You MUST use the scope resolution operator (::) to access standard library functions and objects. Do not use the using namespace std; statement.

Exercise12

Write a program that generates and prints 10 random numbers between 1 and 100. The program should use the srand, time, and rand predefined functions. The program should not use the using namespace std; statement, but instead use the scope resolution operator (::). Each random number should be printed on a new line with a message indicating its position in the sequence.

Best Programming Practices

In real‑life C++ development, large systems often involve writing many functions, each handling a specific task. The main function typically serves only as the program’s entry point, starting the system, so it’s often called a driver function. During testing, especially when the full system isn’t complete, it’s helpful to use stub functions. A stub is a minimal placeholder function (often empty or returning a fixed value) that stands in for a function not yet implemented. By using stubs, you can compile and test parts of the system even before all the functions are fully written. Once development progresses, the stubs are replaced with real implementations.

In the development life cycle, developers always wonder how much testing is enough. The rule of thumb here is to test for all valid and invalid inputs for a function, including what is known as boundary values.

Wrap up!

In C++, functions are a fundamental building block of the language, and understanding their complexities is crucial for effective programming. One such complexity is the difference between Call by Value and Call by Reference. Call by Value involves passing a copy of the variable to the function, meaning any modifications made within the function do not affect the original variable. Conversely, Call by Reference passes the actual variable to the function, so any changes made within the function directly affect the original variable.

Another important concept is the scope of variables. Variables in C++ can be either local or global. Local variables are declared within a function and can only be accessed within that function. In contrast, global variables are declared outside all functions and can be accessed by any function in the program. Understanding the scope is essential for managing data and avoiding conflicts or unexpected behavior in your code.

Functions in C++ can also call other functions. This capability is particularly useful in larger programs where tasks can be broken down into smaller, more manageable functions. A function that is called by another function is often referred to as a helper function.

Function overloading is another powerful feature in C++. It allows you to define multiple functions with the same name but different parameters. The compiler determines which function to use based on the number, types, and order of the arguments. This feature enables varied functionality under a single function name, making your code more readable and organized. Default arguments in functions provide flexibility by allowing optional parameters. If an argument is not provided when the function is called, the default value is used. This feature can make your functions more versatile and easier to use.

Lastly, the scope resolution operator (::) is used to access global variables or functions. This operator is especially useful when a local variable has the same name as a global variable. By using the scope resolution operator, you can ensure the correct variable is referenced in your code. This operator is also used to avoid using an entire namespace and include only the needed names.

In summary, these advanced concepts deepen the understanding of functions in C++, which is essential for writing efficient and effective code.

Answers Key

Exercise 01:

No, the code did not swap the numbers as expected.

The reason is that the function swap passed the arguments a and b by value. This means a and b variables swapped inside the swap function are different copies from the original a and b arguments declared inside the main function.

To fix this error, we only need to change the swap function header to pass arguments by reference. The rest of the code will remain the same.

void swap(int &x, int &y)

Exercise 02:

ch05_exe02

Exercise 03:

ch05_exe03

Exercise 04:

ch05_exe04

Exercise 05:

ch05_exe05

Exercise 06:

ch05_exe06

Exercise 07:

ch05_exe07

Exercise 08:

ch05_exe08

Exercise 09:

ch05_exe09

Exercise 10:

The first call will raise the base of 5 to the default power of 2, resulting in 25.
The second call will raise the base of 5 to the power of 3, resulting in 125.
If you call the calculatePower function without providing any arguments, an error will be thrown specifying that at least one argument must be provided.

ch05_exe10

Exercise 11:

ch05_exe11

Exercise 12:

ch05_exe12

End of Chapter Exercises

  1. Write a void min_max function that takes two integer parameters x and y passed by value and two integer parameters min and max passed by reference. The function should compare x and y and assign the smaller value to min and the larger value to max. The function should not return a value, as the min and max variables should be updated directly via the passed references. The main function will ask the user to enter two integers and test whether x and y are equal. If x and y are equal, the main should print this message and quit the program: “Both entered integers are equal!”; otherwise, call the min_max function, pass the two entered integers, and print out the results.
  2. Write a void sum_ave function that takes two double parameters, sum and average, passed by reference. The sum_ave function should ask users to enter as many positive numbers (whole or decimal) as they want and calculate their sum and average. The sum_ave function should not return any values. The main function will call sum_ave and print out the results.
  3. True or False, both call by value and call by reference allow changes to the arguments in the calling function.
  4. True or False, call by reference is more efficient than call by value for large data structures.
  5. Which of the following correctly describes call-by-value?
    A. The actual parameter is copied into the formal parameter.
    B. The formal parameter is a reference to the actual parameter.
    C. Both A and B
    D. Neither A nor B
  6. When should you use call-by-value?
    A. When you want to modify the passed argument.
    B. When you don’t want to modify the passed argument.
    C. When you want to pass a large data structure.
    D. All of the above
  7. True or False, a local variable can be accessed from anywhere in the program.
  8. True or False, a global variable and a local variable can have the same name.
  9. True or False, changing the value of a global variable will affect all functions that use this variable.
  10. Which of the following correctly describes the global variable?
    A. A variable that is declared within a function.
    B. A variable that is declared outside all functions.
    C. A variable that is declared within a block of code.
    D. None of the above.
  11. What is the default value of a global variable if it is not initialized?
    A. 0
    B. NULL
    C. Depends on the type of the variable
    D. None of the above
  12. What happens if a local variable has the same name as a global variable?
    A. Compile error
    B. The global variable is used.
    C. The local variable is used.
    D. None of the above
  13. Declare a global variable and assign it a value. The main function must do the following:
    1. Print out the global variable value.
    2. Call the increment() function that increments and prints the incremented value of the global value.
    3. Call the decrement() function that decrements and prints the decremented value of the global value.
  14. When you run the attached code, a sequence of values will be printed out to the console for the variable i. Uncomment line 8 and rerun the code. What will happen? Explain why? Fix the generated error.ch05_exercise_14
  15. True or False, a function in C++ can call itself.
  16. True or False, a function can not call multiple other functions.
  17. What happens when a function is called?
    A. The program jumps to the first line of the main function.
    B. The program jumps to the first line of the called function.
    C. The program jumps to the last line of the called function.
    D. None of the above
  18. Write a program where a function printMessage() calls another function getMessage() to get a message and then prints it.
  19. Which of the following is not a correct way to overload a function?
    A. By changing the return type of the function.
    B. By changing the number of parameters of the function.
    C. By changing the types of parameters of the function.
    D. All of the above are correct ways to overload a function.
  20. Which of the following can be used to distinguish overloaded functions?
    A. The return type of the functions.
    B. The name of the functions.
    C. The number and types of parameters of the functions.
    D. None of the above.
  21. Which of the following correctly describes function overloading?
    A. Functions that have the same name and the same number and types of parameters.
    B. Functions that have the same name but different numbers or types of parameters.
    C. Functions that have different names but the same number and types of parameters.
    D. None of the above.
  22. Write two functions with the same name findMax that find the maximum of two integers and two doubles, respectively. Write a main function to test the overloaded findMax functions.
  23. Overload the fullName function to print full names in two different formats, firstName, lastName and firstName, middleInitial. lastName. The main function should ask the user to enter either their first name and last name, or their first name, middle initial, and last name, and then call the corresponding overloaded function.
  24. True or False, functions can have both default and non-default arguments.
  25. True or False, parameters with default values must always be placed first in functions’ definitions.
  26. True or False, the default value of a parameter can not be modified in the function call.
  27. What is the output of the following code?ch05_exercise_27
    A) 10 20
    B) 30 20
    C) 30 10
    D) 10 30
  28.  Write the calculateArea function that takes two integer parameters: length and width, and returns an integer. The width parameter has a default value of -1. When calculateArea function is called, it must check for the width value; if it equals -1, this means that the user did not provide a width value, and it should assume the area required to calculate is the area of a square. Otherwise, the area returned should be width X length. In the main function, call calculateArea function twice, one with only one argument and another call with two arguments.
  29. The scope resolution operator :: can be used to access global variables when a local variable has the same name.
  30. What is the output of the following code? ch05_exercise_30
    A) 10
    B) 20
    C) Error
    D) None of the above

Projects

Project 01: Number Guessing Game:
The objective of this project is to create a simple number-guessing game. The game will generate a random number, and the player will have a limited number of guesses to guess the correct number.

Your program should include the following features:

  • The scope resolution operator :: must be used throughout the entire project code.
  • Global Constant: Define a global constant for the maximum number of guesses allowed.
  • Global Variables: Define global variables for the number to be guessed and the number of guesses made.
  • In the main function, start the game by calling the startGame function and enter a game loop where the player can make guesses until they run out of attempts or guess correctly. If the player guesses the number correctly, it prints “You win!” and the game statistics. If the player runs out of guesses, it prints “You lose!” and the game statistics.
  • startGame(): This function should initialize the game’s global variables. generateRandomNumber function should be called.
  • generateRandomNumber(int min, int max): This function should generate a random number within a given range. The range is defined by the parameters min and max. The generated number must be different in every run of the game.
  • makeGuess(int guess, int& numGuesses): This function should take a guess and a reference to the number of guesses, increment the number of guesses, and return true if the guess is correct.
  • printStatistics(int numGuesses): This function should print the number of guesses made and the number to be guessed. Format the output appropriately.
  • Ensure that the program is well-documented, providing comments for each function and explaining the purpose of the code.

 

Projec 02: Membership Discount System:

The goal of this project is to calculate and apply a discount to a price based on a customer’s membership category. The program should support three categories: “gold”, “VIP”, and “non-member”. The discount rates for “gold” and “VIP” members should be 20% and 30%, respectively, while “non-members” should receive no discount. The project should include at the minimum the following functions:

  • calculateDiscountedPrice(double& price, double discountRate): This function calculates the discounted price of a product. It takes two parameters: price (the original price of the product) and discountRate (the discount rate to be applied). The discounted price is stored in the price variable, which is passed by reference, so the change will affect the original variable in the calling function.
  • getDiscountRate(string category): This function determines the discount rate based on the membership category. It takes one parameter: category (the membership category of the customer). If the category is “gold”, the function returns a discount rate of 0.2 (or 20%). If the category is “VIP”, it returns a discount rate of 0.3 (or 30%). For any other category, it returns the default discount rate of 0.0 (or 0%), which must be declared as a global constant.
  • applyDiscount(double& price, string category): This is an overloaded function that applies a discount to a price based on a specified membership category. It takes two parameters: price (the original price of the product) and category (the membership category of the customer). The function first gets the appropriate discount rate by calling the getDiscountRate function. It then calculates the discounted price using the calculateDiscountedPrice function. Finally, it prints out the discounted price.
  • applyDiscount(double& price): This is another overloaded version of the applyDiscount function. It applies a discount to a price without a specified membership category. It takes one parameter: price (the original price of the product). The function sets the category to “without” and then calls the other version of the applyDiscount function to calculate and print the discounted price.
  • main(): It first asks the user to enter the original price of a product. Then it asks the user if they have a membership. If the user answers “yes”, it asks for the membership category and applies a discount based on that category. If the user answers anything other than “yes”, it applies a discount without a specified category. Finally, it prints out the original price and ends the program.
  • Ensure that the program is well-documented, providing comments for each function and explaining the purpose of the code.

 

Project 03: Grade Statistics Calculator:

The objective of this project is to create a program that calculates and displays statistics for a set of students’ grades.

The project should include at the minimum the following:

    • Use the scope operator :: instead of including the entire standard namespace std.
ch05_fig03
Figure 03: Project 3 output screenshot.
  • Declare all grades as global constants: grade1, grade2, grade3, grade4, and grade5. Initialize these constants to a set of grades of your choice.
  • Create the calculateAverage function to calculate and return the average of the grades.
  • Create the findMax function to find and return the maximum grade.
  • Create the findMin function to find and return the minimum grade.
  • Create the calculateRange function to calculate and return the range of the grades. The range is the difference between the max and min grades.
  • In the main function, display each grade, along with a separator, and then print the calculated statistics as shown in the screenshot.
  • Ensure that the program is well-documented, providing comments for each function and explaining the purpose of the code.

 

License

Icon for the Creative Commons Attribution 4.0 International License

Introduction to C++ ( Volume I ) Copyright © 2025 by Hussam Ghunaim, Ph.D. is licensed under a Creative Commons Attribution 4.0 International License, except where otherwise noted.