Hour 5. Calling Functions

What Is a Function?

A function is a section of a program that can act on data and return a value. Every C++ program has at least one function, the main() function called automatically when the program runs. This function can contain statements that call other functions, some of which might call others, and so on.

Each function has a name that’s used to call the function. The execution of the program branches to the first statement in that function and continues until it reaches a return statement or the last statement of the function. At that point, execution resumes at the place where the function was called.

A well-designed function performs a specific task. Complicated tasks should be broken down into multiple functions, which each can be called in turn. This makes your code easier to understand and maintain.

Declaring and Defining Functions

Before you can write the code for a function, you must declare it.

A function declaration tells the compiler the function’s name, the type of data the function produces, and the types of any parameters received by the function. A function’s declaration, which also is called its prototype, contains no code.

The declaration tells the compiler how the function works. The function prototype is a single statement, which ends with a semicolon.

The argument list is a list of each parameter and its type, separated by commas.

Here’s a declaration for a function that determines a rectangle’s area using length and width parameters:

int findArea(int length, int width);

The three parts of the declaration are the following:

• The return type, int

• The name, findArea

• The type and name of two parameters, an int named length and an int named width

The function prototype must match the three elements of the function or the program won’t compile. The only thing that does not need to match is the name of the parameters. A function declaration doesn’t need to name parameters at all. The previous declaration could be rewritten as follows:

int findArea(int, int);

Although this is permitted, it makes the function prototype less clear than if parameters names had been used.

The function’s name is a short identifier that describes the task it performs. Because the name cannot contain spaces, a common convention is to capitalize each word in the name except for the first. That’s why the A is capitalized in the name findArea.

All functions are structured the same as a program’s main() function. The statements in the function are enclosed within an opening { brace and a closing } brace. If the function returns a value, there should be at least one return statement that returns a literal or variable of the proper return type.

Any C++ data type can be returned by a function. If a function doesn’t produce a value, the declaration should use void as the type. A function that returns void does not need a return statement, although one can still be used, as in this statement:

return;

Unlike the function declaration, the statement naming the function must not end with a semicolon.

Here’s a definition of findArea() that determines the area of a rectangle by multiplying its length by its width:

int findArea(int l, int w)
{
    return l * w;
}

The only statement in the function returns the value of the two parameters multiplied by each other.

The Area program in Listing 5.1 uses this function.

Listing 5.1 The Full Text of Area.cpp


 1: #include <iostream>
 2:
 3: int findArea(int length, int width); // function prototype
 4:
 5: int main()
 6: {
 7:     int length;
 8:     int width;
 9:     int area;
10:
11:     std::cout << " How wide is your yard? ";
12:     std::cin >> width;
13:     std::cout << " How long is your yard? ";
14:     std::cin >> length;
15:
16:     area = findArea(length, width);
17:
18:     std::cout << " Your yard is ";
19:     std::cout << area;
20:     std::cout << " square feet ";
21:     return 0;
22: }
23:
24: // function definition
25: int findArea(int l, int w)
26: {
27:      return l * w;
28: }


When the program is compiled and run, it produces the following output:

How wide is your yard? 15

How long is your yard? 19

Your yard is 285 square feet

The function prototype for the findArea() function is on line 3. The code for the function is contained on lines 25–28. Compare the prototype’s name, return type and parameter types: They are the same, but the names of the parameters are length and width in the prototype and l and w in the function. This distinction does not matter because the parameter types match.


By the Way

If the definition of the function in lines 25–28 were to be moved above its invocation, no prototype would be needed. Although this is a workable solution in small programs like the ones created in this book, on larger programming projects it will be more cumbersome to ensure that all functions are defined before they are used. Declaring all functions with prototypes frees you from having to think about this issue.


Using Variables with Functions

A function works with variables in several different ways. Variables can be specified as arguments when calling a function. Variables can be created in a function and cease to exist when the function completes. Variables also can be shared by the function and the rest of a program.

Local Variables

A variable created in a function is called a local variable because it exists only locally within the function itself. When the function returns, all of its local variables are no longer available for use in the program.

Local variables are created like any other variable. The parameters received by the function are also considered local variables. The Temperature program in Listing 5.2 uses local variables to convert a temperature value expressed in Fahrenheit scale to one using Celsius.

Listing 5.2 The Full Text of Temperature.cpp


 1: #include <iostream>
 2:
 3: float convert(float);
 4:
 5: int main()
 6: {
 7:     float fahrenheit;
 8:     float celsius;
 9:
10:     std::cout << "Please enter the temperature in Fahrenheit: ";
11:     std::cin >> fahrenheit;
12:     celsius = convert(fahrenheit);
13:     std::cout << " Here's the temperature in Celsius: ";
14:     std::cout << celsius << " ";
15:     return 0;
16: }
17:
18: // function to convert Fahrenheit to Celsius
19: float convert(float fahrenheit)
20: {
21:     float celsius;
22:     celsius = ((fahrenheit - 32) * 5) / 9;
23:     return celsius;
24: }


Here’s output produced by running the program three times with the user input Fahrenheit values 212, 32, and 85:

Please enter the temperature in Fahrenheit: 212
Here's the temperature in Celsius: 100

Please enter the temperature in Fahrenheit: 32
Here's the temperature in Celsius: 0

Please enter the temperature in Fahrenheit: 85
Here's the temperature in Celsius: 29.4444

This program has a convert() function defined in lines 19–24 that takes one argument: a float value called fahrenheit.

A local variable named celsius is declared in line 21 and assigned a value in line 22. The value is determined using the three-step formula for converting Fahrenheit to Celsius:

• Subtract 32 from the number.

• Multiply the result by 5.

• Divide that result by 9.

The converted value is returned by the function in line 23. When the function ends, the local variables fahrenheit and celsius cease to exist and no longer can be used.

In the main() method, a variable named fahrenheit is created to hold the value input by a user. A variable named celsius holds the converted version of that temperature. These variables have the same names as the local variables in the convert() function, but they are different variables.

The reason they are not the same is because they are created in a different scope. The scope of a variable is the portion of the program in which a variable exists. Scope determines how long a variable is available to your program and where it can be accessed. Variables declared within a block have the scope of that block. When the block ends with an ending } bracket, the variable becomes unavailable.

You can declare variables within any block, such as an if conditional statement or a function.

Global Variables

Variables can be defined outside of all functions in a C++ program, including the main() function. These are called global variables because they are available everywhere in the program.

Variables defined outside of any function have global scope and thus are available from any function in the program, including main().

The Global program in Listing 5.3 is a revised version of Temperature that makes use of global variables.

Listing 5.3 The Full Text of Global.cpp


 1: #include <iostream>
 2:
 3: void convert();
 4:
 5: float fahrenheit;
 6: float celsius;
 7:
 8: int main()
 9: {
10:
11:     std::cout << "Please enter the temperature in Fahrenheit: ";
12:     std::cin >> fahrenheit;;
13:     convert();
14:     std::cout << " Here's the temperature in Celsius: ";
15:     std::cout << celsius << " ";
16:     return 0;
17: }
18:
19: // function to convert Fahrenheit to Celsius
20: void convert()
21: {
22:     celsius = ((fahrenheit - 32) * 5) / 9;
23: }


When compiled and run, this program performs exactly like the Temperature program, despite the fact that the code has several significant differences.

The float variables fahrenheit and celsius are declared in lines 5–6, outside the main() function and convert() function. This makes them global variables that can be used anywhere without regard to scope.

Because the variables are global, the convert() function takes no parameters and uses the global fahrenheit to convert a Celsius value. The function also returns no value, using void as its return type, because it stores the converted temperature in the global celsius.

Although global variables might seem useful in this example, the practice is asking for trouble in more complex programs that you create. Global variables are avoided because they lend themselves to errors that are difficult to find. A value of a global variable can be changed on any statement in the program, so if there’s an error you must check line by line until the error is found.

One of the advantages of variable scope is that it limits the section of a program that must be checked when a variable either contains a value you weren’t expecting or has been used improperly.

The Global program is the only one in this book that makes use of global variables.

Function Parameters

A function receives information in the form of function parameters. There can be more than one parameters as long as they are separated by commas, or a function can be called with no parameters at all. The parameters sent to a function don’t have to be of the same data type. A function can be called with an integer, two longs, and a character as parameters, for instance.

Any valid C++ expression can be a function parameters, including constants, mathematical and logical expressions, and other functions that return a value.

The parameters passed to a function are local variables within that function, even if they have the same name as variables within the scope of the statement calling the function.

Consider the following sample code, which appears to swap the values of two variables:

int x = 4, y = 13;
swap(x, y);

void swap(int x, int y) {
    int temp = x;
    int x = y;
    int y = temp;
}

Contrary to what you might expect, the swap() function does not swap the variable values so that x equals 13 and y equals 4. Instead, the variables keep their original values. The reason is that the parameters received by the swap() function are local variables within that function. Changing their values does not affect the variables with the same name that were created right before swap() was called.

Changes made to function parameters do not affect the values in the calling function. This is called passing by value because values are passed to the function and a local copy is made of each parameter. These local copies are treated just as any other local variables.

The swap() function swaps the local variables received by the function as parameters, leaving the variables used to call swap() unchanged.

Because variables are passed by value to functions, the swap() function does not work.

Beginning in Hour 10, “Creating Pointers,” you’ll learn several alternatives to passing by value that enable functions to change variables passed to them.

Returning Values from Functions

Functions return a value or void, a data type that represents a nonvalue in C++.

To return a value from a function, the keyword return is followed by the value to return. The value can be a literal, a variable, or an expression, because all expressions produce a value. Here are some examples:

return 5;
return (x > 5);
return (convert(fahrenheit));

These are all permitted return statements, assuming that convert() returns a value. The value returned in the second statement is false if x is less than or equal to 5, true otherwise.

When a return statement is executed, program execution returns immediately to the statement that called the function. Any statements following return are not executed.

It is permissible to have more than one return statement in a function. This is demonstrated by the LeapYear program in Listing 5.4.

Listing 5.4 The Full Text of LeapYear.cpp


 1: #include <iostream>
 2:
 3: bool isLeapYear(int year);
 4:
 5: int main()
 6: {
 7:     int input;
 8:     std::cout << "Enter a year: ";
 9:     std::cin >> input;
10:     if (isLeapYear(input))
11:     {
12:         std::cout << input << " is a leap year ";
13:     }
14:     else
15:     {
16:         std::cout << input << " is not a leap year ";
17:     }
18:     return 0;
19: }
20:
21: bool isLeapYear(int year)
22: {
23:     if (year % 4 == 0)
24:     {
25:          if (year % 100 == 0)
26:          {
27:               if (year % 400 == 0)
28:               {
29:                    return true;
30:               }
31:               else
32:               {
33:                    return false;
34:               }
35:          }
36:          else
37:          {
38:               return true;
39:          }
40:     }
41:     else
42:     {
43:          return false;
44:     }
45: }


The LeapYear program determines whether a year is a leap year. Here’s the output from four successive runs with different user input:

Enter a year: 2010
2010 is not a leap year
Enter a year: 2012
2012 is a leap year

Enter a year: 2100
2100 is not a leap year

Enter a year: 2000
2000 is a leap year

Leap years, which have 366 days rather than 365, follow three rules:

• If the year is divisible by 4, it is a leap year,

• Unless the year is also divisible by 100, in which case it is not a leap year,

• Unless the year is divisible by 400, and it’s a leap year after all.

The isLeapYear() function in lines 21–45 uses several if and else statements to carry out these rules. The function returns the bool value true if a year is a leap year and false otherwise. The function takes an integer argument, the year to check.

There are four different return statements in the function, each of which ends the execution of the function in a different circumstance. Unlike other functions, the last line is not a return statement.

Default Function Parameters

When a function is declared in a prototype to receive one or more parameters, the function only can be called with arguments of the proper data types. Consider a function that takes one integer:

bool isLeapYear(int year);

The isLeapYear() function must take an integer as the parameter, a requirement the compiler will check. Calling a function with a missing or invalid value causes a compiler error.

There’s one exception to this rule: If the function prototype declares a default value for a parameter, the function can be called without that parameter. The default value is used whenever the parameter is omitted. Here’s a revised prototype of isLeapYear() that includes a default year:

bool isLeapYear(int year = 2011);

If the isLeapYear() function is called without specifying a year, the default of 2011 is used.

A function’s definition does not change when default parameter are declared in the prototype.

When a function has more than one parameter, default values are assigned based on the order of the parameters. Any parameter can be assigned a default value, with one important restriction: If a parameter does not have a default value, no previous parameter may have a default value.

Here’s a prototype with four parameters:

long set4DPoint(int x, int y, int z, int t);

The following change is not permitted:

long set4DPoint(int x, int y, int z = 1, int t);

The reason it doesn’t work is because the t parameter lacks a default value. Here’s a permitted prototype:

long set4DPoint(int x, int y, int z = 1, int t = 2000);

The function created from this prototype could be called with this statement:

set4DPoint(130, 85);

The argument values would be 130 for x, 85 for y, 1 for z, and 2000 for t.

The AreaCube program in Listing 5.5 calculates the area of a three-dimensional cube, using default function parameter values for two of the dimensions.

Listing 5.5 The Full Text of AreaCube.cpp


 1: #include <iostream>
 2:
 3: int findArea(int length, int width = 20, int height = 12);
 4:
 5: int main()
 6: {
 7:     int length = 100;
 8:     int width = 50;
 9:     int height = 2;
10:     int area;
11:
12:     area = findArea(length, width, height);
13:     std::cout << "First area: " << area << " ";
14:
15:     area = findArea(length, width);
16:     std::cout << "Second area: " << area << " ";
17:
18:     area = findArea(length);
19:     std::cout << "Third area: " << area << " ";
20:     return 0;
21: }
22:
23: int findArea(int length, int width, int height)
24: {
25:     return (length * width * height);
26: }


The program produces the following output:

First area: 10000

Second area: 60000

Third area: 24000

On line 3, the findArea() prototype specifies that the function takes three integer parameters, the last two with default values.

The function computes the area of a cube. If no height is provided, a height of 12 is used. If no width is provided, a width of 20 and height of 12 are used. It is not possible to provide a height without providing a width.

On lines 7–9, the dimensions length, height, and width are initialized. They are passed to the findArea() function on line 12. The values are computed, and the result is displayed on line 13.

On line 15 findArea() is called again with no value for height. The default value is used and the area of the computed cube is displayed.

On line 18, findArea() is called with neither width nor height. Execution branches off for a third time to line 25. The default values for both are used and the cube’s area is displayed.

Overloading Functions

In C++, more than one function can have the same name as long as there are differences in their arguments, a practice called function overloading. The functions must have different data types for parameters, a different number of parameters, or both. Here are three prototypes for overloaded functions:

int store(int, int);
int store(long, long);
int store(long);

The store() function is overloaded with three different parameter lists. The first and second differ in the data types and the third differs in the number of parameters.

The parameters the function is called with determine which function will be called.

The return types for overloaded functions do not factor into whether they are different. Several overloaded functions can have the same return type, as in the preceding example, or different types. You can’t overload by making the return different, however. The parameter types or number of parameters must differ.

Function overloading also is called function polymorphism.

Overloading makes it possible to create a function that performs a similar task on different types of data without creating unique names for each function. If your program needs to average two numbers expressed in different formats, it could have functions named averageInts(), averageDoubles(), and averageFloats().

To simplify, an overloaded function called average() could be used with these prototypes:

int average(int, int);
long average(long, long);
float average(float, float);

You just pass in the right data when calling average() and the proper function is called.

Inline Functions

When you define a function, the C++ compiler creates just one set of instructions in memory. Execution of the program jumps to those instructions when the function is called and jumps back after the function returns to the next line in the calling function. If the program calls the function 10 times, it jumps to the same set of instructions each time. There is only one copy of the instructions that make up the function, not 10 copies.

Some performance overhead is required to jump in and out of functions. When a function consists of a small number of statements, you can gain some efficiency if the program avoids making the jumps. The program runs faster if the function call can be avoided.

If a C++ function is declared with the keyword inline, the compiler does not create a real function. Instead, it copies the code from the inline function directly into the place where the function was called. It is just as if you had written the statements of the function right there.

If an inline function is called 10 times, the inline code is copied all 10 times. The tiny improvement in speed could be swamped by the increase in size of the executable program.


By the Way

The inline keyword is a hint to the compiler that you would like the function to be inlined. The compiler is free to ignore the hint and make a real function call. Current compilers do a terrific job on their own of making C++ code execute quickly, so there’s often little to be gained from declaring a function inline.


The inline keyword is used in the function prototype:

inline int double(int);

The function itself does not change:

int double(int target)
{
    return 2 * target;
}

Summary

Functions are the workhorses of a C++ program. Every task a program performs is represented by a function. When that task can be broken down into smaller tasks, each of those can be a function, as well.

All functions should be declared using function prototypes, which are statements that identify the function’s name, the order and data type of parameters, and the data type that it returns.

The functions in this hour’s programs are named like commands: findArea(), convert(), and average(). Functions are commands. You’re telling the computer to find an area, convert a value, and average two numbers.

Each function that you write in a program is a discrete task. The more specific the task, the shorter the function will be.

Function overloading makes it possible to use the same function name on several related functions. The data type and number of parameters to the function are used to differentiate between them. The C++ compiler figures out which function to execute based on these differences.

Q&A

Q. Why not make all variables global?

A. Although this used to be common, as programs became more complex it became extremely difficult to find bugs in programs with globally accessible variables. The reason is because data could be misused in any part of the program. To minimize the chance of error and make debugging easier, data should be kept in as local a scope as possible.

Q. Why aren’t changes to the value of function parameters reflected in the calling function?

A. Parameters passed to a function are passed by value. That means that the parameter received by the function is actually a copy of the original value, even if it shares the same variable name. You learn how to pass a changeable parameter to a function when you begin working with pointers and references in Hour 10.

Q. What happens if I have the following two functions?

int findArea(int width, int length = 1);
int findArea(int size);

Will these overload? There are a different number of parameters, but the first one has a default value.

A. The declarations will compile, but calling the findArea() with one parameter will cause a compile-time error about “ambiguity between findArea(int, int) and findArea(int).”

Q. Why doesn’t anyone ever use a No. 1 pencil?

A. Pencils are produced according to a grading system that rates them based on how soft the lead is, which dictates the darkness of the mark it produces. The softer the lead, the darker the mark.

The grades of pencils sold in the United States are No. 1, 2, 2.5, 3, and 4.

The No. 1 produces the darkest mark, but the soft lead breaks more easily and requires sharpening more often. The No. 4 has the hardest lead and produces the faintest mark.

No. 2, by striking a balance between lead softness and mark darkness, is so popular that most people aren’t aware any of the others exist.

The others can be found at art supply stores, though they may use another grading system entirely that uses “B” for the softest leads and “H” for the hardest. The range goes “9B”, “8B”, “7B” on down to a middle value of “B”, then goes from “H” to “2H”, “3H”, “4H” up to a maximum of “9H”.

Also, pencil leads aren’t made of lead. They’re graphite.

Who knew pencils were so complicated? If you need to take notes while you figure all of this out, use a No. 2 pencil.

Workshop

Now that you’ve had the chance to see functions, you can answer a few questions and do a couple of exercises to firm up your knowledge of function prototypes and function parameters.

Quiz

1. What happens when a variable is a parameter received by a called function?

A. A copy of the variable is passed to the function.

B. The actual variable is passed to the function.

C. A compiler error occurs.

2. When three overloaded functions have the same name, how does C++ know which one to call?

A. The function’s return types are different.

B. The functions parameters are different.

C. The order of the function from top to bottom.

3. How many values can a return statement return?

A. One

B. One or more

C. None or one

Answers

1. A. A copy is made of the variable, which is called passing by value. Any changes to the variable within the function do not affect the original variable.

2. B. Overloaded functions must have parameters of different types or a different number of parameters. The compiler uses this difference to determine which function to call. The return type of the functions does not matter.

3. C. A function can return no more than one value, although the function can have several return statements to return that value. When a function does not return any value, the void data type indicates this fact.

Activities

1. Write a program that converts a temperate from Celsius to Fahrenheit. The formula to do this is to multiply the Celsius temperate by 9, divide the result by 5, and then add 32.

2. Write a program that can calculate the average of two integers, two long integers or two floating-point values using an overloaded function named average().

To see solutions to these activities, visit this book’s website at http://cplusplus.cadenhead.org.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.12.3