Revised: January 28, 2007
By
Richard G. Baldwin
File: Pf00108.htm
Practice Text
This lesson was written specifically for the benefit of my students in COSC 1315, Fundamentals of Programming. The lesson was written under the assumption that those students have no prior programming knowledge when they enroll in the course.
Which came first, the blinker switch or the car?
If you have looked into very many programming fundamentals textbooks, you may have noticed that most authors tend to defer the discussion of functions until they have discussed lots of other details about the programming language. I disagree with that approach.
Rather than learning first about the blinker switch and later learning about the car, I believe it is best to first learn about the car and then to drill down into the details to finally learn about the blinker switch. Therefore, my approach is to teach programming starting with the big picture and working down to the details instead of the other way around.
Functions are definitely a part of the big picture, and it is easy to understand them without having to know about the programming blinker switches.
Another browser window
I recommend that you open another copy of this document in a separate browser window so that you can view the code and the discussion of that code at the same time.
Functions have been used in computer programming since the early days of programming. Programming structures that resemble functions are also often called by other names such as
Calculate the square root
Suppose that your program needs to calculate the square root of a number. Referring back to your high-school algebra book, you could refresh your memory on how to calculate a square root.
Design the algorithm and write the code
Then you could design the algorithm describing that process. Having the algorithm available, you could write the code to calculate the square root and insert it into your program code.
Compile and execute your program
Then you could compile, and run your program.
If you did it all correctly, your program should correctly calculate the square root.
(For reasons that will become apparent later, I will refer to the code that you inserted into your program as in-line code.)
Oops, you need to do it all over again
Suppose that further on in your program you discover that you need to calculate the square root of another number.
And later, you discover that you need to calculate the square root of still another number.
Could copy and paste your square root code
Obviously, with a few changes, you could copy your original code that computes the square root and insert it as in-line code at each location in your program where you need to calculate the square root of a number.
Is there a better way?
After doing this a few times, you might start asking yourself if there is a better way. The answer is "yes, there is a better way."
A function provides a better way
The better way is to create a separate program module that has the ability to calculate the square root and to make that module available for use as a helper to your main program each time your main program needs to calculate a square root.
Functions and methods
In C++, this separate program module is called a function. In Java and C#, it is usually called a method.
Sharing the workload among different programmers
In addition to making it possible to avoid the unnecessary duplication of inline code, modularization of a large program also makes it possible to share the programming workload among the different members of a programming team.
Each member of the team can take on the task of writing one or more of the required functions. Then every member of the programming team can use the functions written by herself and by others on the programming team.
(Later, I will illustrate the use of a standard or prewritten function for calculating the square root of a number.)
You can also write your own functions
In addition, if you need a function to perform some task for which there is no available prewritten function, you can always write your own function.
Normally, when designing and writing a function such as one that calculates the square root of a number, it is desirable to write it in such a way that it can calculate the square root of any number (as opposed to only one specific number). This is accomplished through the use of something called parameters.
Passing by value or by reference
Parameters provide the mechanism by which one function can pass information to another function.
In C++, parameters can be passed to functions either by value or by reference.
(I will have more to say about this later in conjunction with the explanation of the sample programs.)
Calling (invoking) a function
The process of causing a function to be executed is commonly referred to as calling or invoking the function.
Pass me the number please
When your program calls the square root function, it will need to tell the function the value of the number for which the square root is needed.
In general, many functions will require that you provide certain kinds of information when you invoke them. The code in the function needs this information to be able to accomplish its purpose.
Passing parameters
This process of providing information to a function when you invoke it is commonly referred to as passing parameters to the function.
For the square root function, you need to pass a parameter whose value is the number for which you need the square root.
An example of a function that performs an action is a function that causes the computer to emit an audible beep. This function would not need to return an answer, because that is not the purpose of the function. The purpose is simply to cause the computer to beep.
Returning an answer
On the other hand, a function that is designed to calculate the square root of a number needs to be able to send the square root value back to the program that called the function.
Can you keep a secret?
After all, it wouldn't be very useful if the function calculated the square root and then kept it a secret.
The process of sending back an answer is commonly referred to as returning a value.
Returned values can be ignored
When functions are designed, they can be designed in such a way that they either
When a function does return a value, the program that called the function can either
For example, in some cases where a function performs an action and also returns a value, the calling program may elect to ignore the returned value.
On the other hand, if the sole purpose of a function is to return a value, it wouldn't make much sense for a program to call that function and then ignore the value that is returned (although that would be technically possible).
We will begin with a program that illustrates the use of a prewritten function for computing the square root of a number. This program is shown in Listing 1.
/*File: Functions01.cpp This C++ program illustrates the use of prewritten functions. This program displays the following text on the screen: 3.87298 4.47214 5 5.47723 ************************************************/ #include <iostream> #include <math.h> //Necessary for sqrt() function using namespace std; int main(){//Global main function. cout << sqrt(15.0) << endl; cout << sqrt(20.0) << endl; cout << sqrt(25.0) << endl; cout << sqrt(30.0) << endl; return 0; }//end main function Listing 1 |
The function named sqrt
The code in Listing 1 invokes the same function four times in succession, passing a different parameter each time, and using cout to display the returned value.
The prewritten function illustrated by Listing 1 is named sqrt. Figure 1 shows the documentation for the sqrt function as obtained from this web site:
double
sqrt ( double x );
Calculate square root. Parameters.
Return Value. Portability. Figure 1 |
As you can see from the documentation in Figure 1, the sqrt function requires a single incoming parameter of type double.
It returns the square root of the incoming value as type double.
Must include a special header file
A program must include the header file named math.h in order to be able to access the sqrt function from the library of functions. Thus the code to accomplish that is included in Listing 1.
Call sqrt four times
As mentioned above, the code in the main function in Listing 1:
The output
The square root value returned by the function is displayed each time it is called, producing the screen display shown below:
3.87298 4.47214 5 5.47723
Very convenient
Listing 1 illustrates the convenience of being able to call a function at multiple locations within a program to perform the same task each time that task needs to be performed.
Listing 1 also illustrates the convenience of being able to call a function that was previously written by you by or someone else without having to write the code into your program.
The main function takes no parameters and returns a value
Every C++ program that runs in a stand-alone mode requires a function named main. Execution of the program begins and ends in the main function.
For simple programs, (which excludes event-driven programs), when the main function runs out of things to do, the program terminates.
Different formats for the main function
There are different formats that can be used for the main function in C++. One of those formats is shown in Listing 1. This format:
Why does it return a value of type int?
I'm not going to get into the reasons for having the main function return a value of type int here. (That is a topic for a more advanced course.) Suffice it to say that this requirement manifests itself in two ways in Listing 1:
Listing 2 shows a program with two user-defined functions that take no parameters and that do not return a value.
/*File: Functions02.cpp This C++ program illustrates functions having no parameters and no return value. This program displays the following text on the screen: Hello World Goodbye cruel world ************************************************/ #include <iostream> using namespace std; //A function prototype is required due to the // placement of the goodbye function relative // to the main function. The main function // requires the prototype in order to understand // the interface to the goodbye function, which // has not yet been defined when it is called. void goodbye();//function prototype //No function prototype is required for the // hello function because it is defined before it // is called. void hello(){ cout << "Hello World" << endl; }//end hello //---------------------------------------------// int main(){//Global main function. hello(); goodbye(); return 0; }//end main function //---------------------------------------------// void goodbye(){ cout << "Goodbye cruel world" << endl; }//end goodbye Listing 2 |
General syntax for a function
The general syntax for a function consists of two main parts:
The function signature
The function signature consists of:
The formal parameter list
The formal parameter list is empty if the function requires no parameters.
That is the case for all three functions in Listing 2. Otherwise, the formal parameter list would contain one or more pairs of words separated by commas.
The parameter declaration
Each pair of words in the formal parameter list (a parameter declaration) consists of the following:
(As you will see later in Listing 4, the formal parameter list may also contain the reference operator & for those cases where parameters are passed by reference.)
Additional sample programs
I will explain a sample program later in this lesson where the formal parameter list is not empty.
I will also explain a sample program where the formal parameter list contains the reference operator &.
C++ code is sensitive to position
C++ code is sensitive to the relative locations of function definitions and the code that calls the functions.
In order for C++ code to successfully call a function, the function must have either been:
A function prototype
A function prototype is used to declare a function to make it possible for code to call the function before it is actually defined. I will explain the syntax of a function prototype later.
main function calls two other functions
The code in the main function in Listing 2 calls two other functions:
Before
The hello function is physically defined ahead of the main function in Listing 2.
Therefore, a function prototype is not required for the main function to be able to call the hello function.
After
The goodbye function is physically defined after of the main function in Listing 2.
Therefore, it was necessary to provide a function prototype to declare the goodbye function in Listing 2 in a location that is physically ahead of the definition of the main function.
Otherwise, the main function would be unable to call the goodbye function.
Syntax of the function prototype
The prototype for a function essentially consists of the signature of the function followed immediately by a semicolon instead of a body.
However, the names of the parameters may be omitted from the function prototype whereas the signature must always provide names for the parameters.
On the other hand, it is not necessary to omit the parameter names from the function prototype.
No parameters or return value required
The functions named hello and goodbye in Listing 2 perform a simple action.
Each function simply displays a message on the screen when called.
These two functions don't require incoming parameters to accomplish their purpose, and they don't return a value.
Transfer the flow of control to the hello function
The main function calls the hello function first. This causes the flow of control in the program to pass from the main function to the hello function.
The hello function displays a message on the screen and then terminates.
Return control to the main function
When the hello function terminates, control is returned to the main function without returning a value in the process.
Hello World
The message that is displayed on the screen by the function named hello consists of the following line of text:
Hello World
Transfer flow of control to the goodbye function
When control is returned to the main function from the hello function, the main function calls the goodbye function passing the flow of control to the goodbye function in the process.
The goodbye function displays the following text on the screen:
Goodbye cruel world
Then the goodbye function terminates, returning control to the main function without returning a value in the process.
Terminate the program
When the flow of control returns to the main function from the goodbye function, there is nothing more for the main function to do.
At that point, the main function terminates, returning a value of 0 to the operating system in the process.
When the main function terminates, the program terminates.
C++ allows a parameter to be passed to a function either by value or by reference. The difference can be very significant as will be illustrated by Listing 3 and Listing 4.
Passing a copy of a variable
When a program passes a variable as a parameter to a function by value
The function can do just about anything that it wants to do with the incoming parameter.
Cannot modify the original variable
However, the one thing that it cannot do with the parameter is to modify the contents of the original variable.
If it modifies the value of the parameter, it simply modifies the copy and doesn't modify the value of the original variable.
Passing an address
When a program passes a variable to a function by reference, that variable's address in memory is passed to the function.
(Maybe it is a copy of the address, but the result is the same in either case.)
Once again, the code in the function can do just about anything that it wants to do with the incoming parameter.
Can modify the original variable
For example, the code in the function can use the address to find and to modify the value stored in the original variable.
(All of the complex address manipulations take place behind the scenes requiring no special effort on the part of the programmer to modify the value stored in the original variable.)
Important
When a variable is passed by reference, if the function modifies the value of the incoming parameter, it doesn't modify the address.
Rather, some complex address manipulations take place behind the scenes causing the code to actually modify the value stored at that address in memory.
This modifies the value stored in the original variable. This will be illustrated later in Listing 4. First, however, we will illustrate the concept of pass by value.
Passing parameters by value
Listing 3 contains a program that illustrates passing two parameters of different types to a function by value.
/*File: Functions03.cpp This C++ program illustrates functions with value parameters and no return value This program displays the following text on the screen: In main a = 10 b = 20.1 In aFunction x = 10 y = 20.1 Change x and y Still in aFunction x = 100 y = 200.1 Back in main a = 10 b = 20.1 ************************************************/ #include <iostream> using namespace std; void aFunction(int x,double y){ cout << "In aFunction" << endl; cout << "x = " << x << " y = " << y << endl; cout << "Change x and y" << endl; x = 100; y = 200.1; cout << "Still in aFunction" << endl; cout << "x = " << x << " y = " << y << endl; }//end aFunction //---------------------------------------------// int main(){//Global main function. cout << "In main" << endl; int a = 10; double b = 20.1; cout << "a = " << a << " b = " << b << endl; aFunction(a,b); cout << "Back in main" << endl; cout << "a = " << a << " b = " << b << endl; return 0; }//end main function Listing 3 |
Behavior of the main function
The main function in Listing 3 declares and initializes two local variables named a and b.
(I will provide an explanation of the different kinds of variables, including local variables, in a future lesson. For now, just think of a local variable as a pigeonhole in memory where a value is stored.)
The types of the two variables are int and double respectively.
(I will also provide more information about the types of variables such as int and double in a future lesson. For now, a variable of type int can contain whole numbers only, such as 9 or 3642. A variable of type double can contain values with fractional parts, such as 1.333.)
Display the variables
Then the main function displays the values contained in the two variables producing the following output on the screen:
In main a = 10 b = 20.1
(Note how insertion operators can be concatenated to cause multiple items to be displayed on the same line.)
Call the function named aFunction
Following that, the main function calls the function named aFunction passing the variables named a and b to the function by value.
The formal parameter list versus the actual parameter list
This will be a good place to discuss the difference between the formal parameter list and the actual parameter list.
As explained earlier, the formal parameter list is part of the function signature, which in this case looks like this:
void aFunction(int x,double y)
The formal parameter list is shown in boldface in the above function signature.
The actual parameter list
The actual parameter list is part of a function call, which in this case looks like this:
aFunction(a,b);
The actual parameter list is highlighted in boldface in the above function call.
How do they differ?
By comparing the two, you can see that:
Display, modify, and display the parameters
When the main method calls aFunction, control is transferred to the function.
The code in the function named aFunction:
The output from aFunction
The function named aFunction produces the following output on the screen:
In aFunction x = 10 y = 20.1 Change x and y Still in aFunction x = 100 y = 200.1
Display the variables again
When control returns to the main function, the main function once again displays the values contained in the two variables that were passed to the function by value.
The output from the main function
This produces the following screen output:
Back in main a = 10 b = 20.1
The important point is that even though the code in the function named aFunction modified the values of its incoming parameters, this had no effect on the values stored in the variables that were passed by value to the function named aFunction.
The program in Listing 4 is identical to the previously-discussed program in Listing 3 with one exception.
The important exception
The exception is the inclusion of the reference operator & in front of the x in the formal parameter list for the function named aFunction.
Not a trivial change
This is not a trivial change. It produces a major difference in the behavior of this program relative to the previous program.
/*File: Functions04.cpp This C++ program illustrates functions with value parameters, reference parameters, and no return value This program displays the following text on the screen: In main a = 10 b = 20.1 In aFunction x = 10 y = 20.1 Change x and y Still in aFunction x = 100 y = 200.1 Back in main a = 100 b = 20.1 ************************************************/ #include <iostream> using namespace std; //Note that the first parameter is a reference // parameter. void aFunction(int &x,double y){ cout << "In aFunction" << endl; cout << "x = " << x << " y = " << y << endl; cout << "Change x and y" << endl; x = 100; y = 200.1; cout << "Still in aFunction" << endl; cout << "x = " << x << " y = " << y << endl; }//end aFunction //---------------------------------------------// int main(){//Global main function. cout << "In main" << endl; int a = 10; double b = 20.1; cout << "a = " << a << " b = " << b << endl; aFunction(a,b); cout << "Back in main" << endl; cout << "a = " << a << " b = " << b << endl; return 0; }//end main function Listing 4 |
First parameter will be pass by reference
The inclusion of the & in front of the parameter name in the formal parameter list specifies that the first parameter named x in the formal parameter list will be passed to the function by reference.
Second parameter will be passed by value
The absence of a similar & with the second parameter specifies that the second parameter will be passed by value as before.
Otherwise, the code in the function named aFunction and the code in the function named main is exactly the same as in the program discussed earlier in conjunction with Listing 3.
Changing the value stored in the original variable
As a result of passing the first parameter to the function by reference -
When the code in aFunction changes the value of the first parameter from 10 to 100 -
That actually changes the value stored in the variable named a that was passed by reference to the function.
The program output
The output produced by the program is shown below:
In main a = 10 b = 20.1 In aFunction x = 10 y = 20.1 Change x and y Still in aFunction x = 100 y = 200.1 Back in main a = 100 b = 20.1
The important difference between this output and the output produced by the previous program is highlighted in boldface.
To reiterate, changing the value of a parameter that was passed by reference changes the value stored in the variable that was passed by reference.
Listing 5 illustrates a program that receives a single incoming parameter by value and returns a value.
(This structure is similar to the sqrt function discussed earlier.)
/*File: Functions05.cpp This C++ program illustrates a function with a parameter and a return value. This program displays the following text on the screen: a = 2 a doubled = 4 ************************************************/ #include <iostream> using namespace std; int doubleIt(int x){ return x + x; }//end doubleIt //---------------------------------------------// int main(){//Global main function. int a = 2; cout << "a = " << a << endl; cout << "a doubled = " << doubleIt(a) << endl; return 0; }//end main function Listing 5 |
Behavior of the function named doubleIt
The behavior of the function named doubleIt is very simple. It returns a value that is double the value that it receives. It computes that return value by adding the incoming value to itself and returning the sum.
Important changes to the code
The most important thing to note about the code in Listing 5 is the following:
Otherwise, the program in Listing 5 is straightforward.
Compatibility issue
If you replace the return statement in the function named doubleIt with either of the following statements, the program will still compile and run successfully using Dev C++ v4.9.9.2 running under WinXP Pro. (This is apparently some kind of a coding shortcut that has been incorporated into Dev C++.) However, the compilation will fail using MS VS 6.0 running under WinXP Pro.
int y = x + x; x + x;
This illustrates some of the compatibility issues that you can expect to encounter when working with C++.
The output
The screen output produced by the program is
a = 2 a doubled = 4
Global functions
The functions illustrated in this lesson are all global functions. That is to say, they are defined outside of a class and are accessible by any code in the program that knows the name of the function.
The use of global functions is not allowed in more modern object-oriented programming languages such as Java and C#.
(Programmers tend to call them methods instead of functions in Java and C#.)
However, functions or methods can be defined inside of classes in C++, Java, and C#, and what you have learned in this lesson is directly applicable to the use of functions or methods that are defined inside of classes.
Copyright 2005, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.
Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.
In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.
Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.
-end-