Three important ideas so far:
Next important idea:
As a first introduction, understand that the main() function will become the "boss" function which calls on "worker" sub-functions to do pieces of the work.
We will learn to use functions written by others and also how to write and use our own. Sets, or "libraries" of such functions are the main way in which the C++ programming language can be extended or expanded. For example, there are libraries of mathematical functions, libraries of graphical functions, libraries of I/O functions, and many more.
These worker sub-functions are relatively small and simple (compared to the whole program, which does a larger, more complicated task than any one of its sub-functions).
We can make an analogy of a building contractor: main()) who hires subcontractors (plumber, roofer, electrician - the functions) to do parts of the whole job. (The plumber may hire sub-sub-contractors to do part of the plumbing job (diggers to get to the sewer, etc.). We will see that in C++ sub-functions can themselves call on other functions to do part of their work.
We decompose a program into functions for several reasons:
Note on terminology:
When one function uses another, we say that it calls the other. For example, if main() uses the sqrt() function to calculate a square root, we say that main() calls sqrt() - and that sqrt() is called by main(). So the boss is the caller function and the worker is the called function.
The called function is a black box that does a job:
Arg1 => => a result
Arg2 =>
Example: a square root function. Takes (i.e. must be given) a real number as an argument and returns its square root:
num => [sqrt fn] => sqrt_of_num
In main() (or the calling program), we code:
sqrt_of_num = sqrt(num);
cout << sqrt(num); or y = (b + sqrt(b*b - 4*a*c))/(2*a);
Notice that from the boss's or caller's point of view, we don't know or care how the function does its work. This leaves the client free to concentrate on overall program logic and structure.
The sqrt() function is in a standard library. In some systems, you must
#include <cmath>
to make this library accessible to your program.
NONE. Except the arguments passed.
That is, the function has no idea what the boss is doing.
It cannot see or use or access the boss's plan, logic, variables, or anything.
The function just does its job with the information (arguments) given to it and it may (depending on how it is written) return a single result (an answer).
divide and conquer strategy.
a function can be called from multiple places in a program. This avoids code duplication.
a function can be used in other programs easily (in a library. Ex: sqrt())
makes maintenance easier, since any change (fix a bug, improve the speed) can be done in one place, not everywhere the code occurs
makes code more reliable, less prone to bugs - more later
a little more up-front planning work
sometimes a few more lines of code
Decide
name
what the function needs to return (if anything) including the data type of the returned result.
what information it needs to do its job (its arguments)
what local or temporary variables it needs to do its job (see below)
the logic of the function
Example 1: we want a function to cube an integer. No error checking.
Name: cube
Returns: an integer - the cube of a number.
Arugments: one argument - the number to be cubed
Local variables: an integer to hold the calculated result
Logic: trivial
int cube( int x ) //See Note 1 { int result; result = x * x * x; return result; // See Note 2 }
Note 1:
int - tells the data type of the value to be returned
cube - the name of the function
int x - tells the data type of the arg, and gives it a name to be used inside the
function. This name may or may not be the name of a variable in the caller. It
doesn't matter.
Note 2:
return value; - here you supply the value to be returned. In this case it must be an integer expression since the function has promised to return an integer.
In boss or caller you could use the function cube() as follows:
int num, ans; num = 2; ans = cube(num); cout << ans; // 8 ans = cube(ans); cout << ans; // 512 ans = cube(3); cout << ans; // 27 cout << cube(ans/3); // 729
Note that 3 different things were passed to cube() in the first three different calls: num, ans, and 3.
Inside cube(), each passed-in value - was called x.
cube() does not know the names of the variables in the caller (num, and ans; and 3 is not even the name of a variable).
In fact, cube() does not even get access to num, ans, or 3 or 9. It gets copies of their values. It uses the copies to do its work. Since it gets copies, it cannot change the original data passed. (Think about this - what would it mean to alter the value of 3???)
Example 2: similar to 1, but we want to return the cube of half the number passed as the argument.
int cube_half( int x ) { x = x / 2; return x * x * x; }
Here x (the "alias" for the argument passed from the caller) has been changed in the function, but understand that only the copy is changed. The original (back in the calling program) is unchanged:
in caller:
num = 4; ans = cube_half(num); cout << ans << num; // displays 8 4
num is still 4. It was not altered by the division within cube_half(). Only the copy of num was halved in cube_half().
Example 3: Sometimes functions don't need to return a value. Most often that's when they just print something.
Then we say they return void.
Here's a function to display a page header with page number at the top of the page. (Assume cout's are sending output to the printer here; note \t = tab and \f = newpage for the printer)
void pageHeader(int pagenum) { cout << "\f\tInventory Control System\t\tpage " << pagenum; cout << "\nDescription\t\tQuantity\t\tPrice"; }
Notes:
1. \f is formfeed = new page
2. \t is a tab
3. nothing is returned; so the return statement is not required, but it can be added with no arguments. In a void function, if there is no return statement, control returns to the calling program after the last line in the function has executed.
In caller, you'd code:
pageHeader(1);
or, better, keep the current page number in an int variable, initialized to 0 and incremented before printing each page:
int pnum = 0; //these two would probably be in a loop that prints 1 page per repetition pnum++; pageHeader(pnum);
Example 4a: write a function to display two dashed lines on the screen. Like this:
-------------------------------
-------------------------------
The number of dashes is determined by the caller by supplying that number as an argument to the function.
void lines(int n) { int i; //NOTE: use of a local variable cout << "\n"; for (i = 1; i <= n; i++) cout << "-"; cout << "\n"; for (i = 1; i <= n; i++) cout << "-"; }
Notes on i:
Example 4b: write a function to display n dashed lines each of which has m dashes in it. So a calling statement would be, for example,
dashes(3, 40); //make 3 lines of 40 dashes
We'll write this as a function dashes() which calls a sub-function (in a loop) oneLine() to do one line:
//display numLines lines of dashes void dashes(int numLines, int numDashes) { int i; for (i = 0; i < numLines; i++) oneLine(numDashes); } //display one line of num dashes on a new line void oneLine(int num) { int i; cout << "\n"; for (i = 0; i < num; i++) cout << "-"; }
Study this example and be sure you understand how it works.
Example 5: Write a function to raise an integer number to an integer power. Assume both arguments are > 0; also assume that the result will fit in a int. Since n-to-the-i could generate a really big value, you would have to be careful that you don't exceed the maximum integer, or perhaps use a special data type with a larger range.
int n_tothe_i(int n, int i) //note 2 args passed { int ndx; int temp; //temp = n to the first power temp = n; //now mult temp by n i-1 times for (ndx = 1; ndx < i; ndx++) temp *= n; return (temp); }
("Play computer" with this to satisfy yourself that it works correctly.)
Sample calling statements:
x = n_tothe_i(3, 2); //3 to the 2 power
y = n_tothe_i(x, 4);
z = n_tothe_i(r, (i*j)); //note use of expr as arg
Example 6: Write a function that returns the (double) average of a sum and count (passed as a double and an int) if the value in count is greater than 0. Have it return 0 if count is 0 or negative. (Yes, I know that n/0 is not defined and shouldn't be set to 0. And that 0/n is 0. This is just for illustration.)
double avg(double sum, int count) { if (count <= 0) { return 0; } else { return sum/count; } }
Note that there are 2 return statements here. The first one executed is the last statement executed in the function. It is an immediate bailout. Some people discourage the use of multiple returns in a function. They argue that in a large function, you might not see all the return statements and so you might misunderstand how the function works. In small simple functions, this is usually not a big problem. However, if you want to have just one return, you could re-write the function as follows. You'll need one local variable:
double avg(double sum, int count) { double temp; if (count <= 0) { temp = 0; } else { temp = sum/count; } return temp; }
One further requirement for using functions:
You must tell the C++ compiler a bit about each function you use in your program before you use it.
After the #includes (and any #defines), but before int main(), add one line for each function that you are writing and using in this source code file.
These lines are called the function prototypes.
Each line is of the following form:
return-type function-name(arg1-type, arg2-type, ...);
The arguments need only be listed by type - you don't have to write a name here. (But you can include a name if you'd like to remind yourself of what the argument stands for.)
If a function takes no arguments, you can just type a set of empty ().
Note the semi-colon at the end of each line. It is required for function prototypes.
Examples - prototypes for all the previous functions
int cube(int); int cube_half(int); void pageHeader(int); void lines(int); void dashes(int, int); void oneLine(int); int n_tothe_i(int, int); double avg(double, int);
Finally, how do all these pieces go together? Here's a skeleton program that uses cube()
#includes... using statement #defines... int cube(int); //**************************************************** int main() { int ans; ans = cube(5); cout << ans; return 0; } //**************************************************** int cube(int c) { int result; result = c * c * c; return result; }
Notes: