Suppose you were writing a program that needed to use a square root. (For readers whose maths is rusty, the square of a number is what you get when you multiply the number by itself. So, for example, the square of 4 is 16. A square root is the opposite. The square root of some number x is that number which, when multiplied by itself, gives you x. So the square root of 16 is 4.)
If the square root is an integer, as in my example, the arithmetic is not too hard. But if the square root is a number with a decimal point in it (a "floating-point number"), it's not so easy. What is the square root of 15? Three point something, but what exactly?
The data types we have looked at so far include integers, strings and booleans (in C++ int, string
and bool
). For doing calculations involving decimal points, we use a data type called a double
. There is also a data type called a float
(for "floating-point number"), which we could also use. A double
occupies twice as much memory as a float
, hence its name. In the days when a computer's memory was very limited, you would use a double
only if you needed the extra precision, but, now that computer memories are so much larger than they used to be, the double
is more often used. In C++ it is the default for representing floating-point numbers. That is to say, if you had an assignment such as d = -0.23;
the -0.23 would, technically, be a double
, not a float
.
Fortunately, we do not have to invent our own algorithm for calculating square roots since C++ provides a square-root function. If we #include <cmath>
then we can use a function called sqrt
. For example, we could say double d = sqrt(15.0);
or cout << sqrt(15.0);
(It's 15.0, a double
, rather than 15, an int
, since the sqrt
function expects to be given a double
.)
The sqrt
function is one of the functions in the cmath
library. That is to say, it is a self-contained unit of program code, placed in one of the standard libraries, which is there for us to use if we want to. It takes number (as a double
) and returns a double
representing the square root of that number. In the example, we give it (or pass it) the number 15.0, and it gives us back (or returns) 3.87298.
Note that we do not know how it does what it does, and we do not need to know. To us, it is a "black box". To use it, we just need to know its name (sqrt
), what it takes (a double
) and what it returns (also a double
). (We also need to know that it is in the cmath
library and to remember to #include <cmath>
. This is one of the nuisances of the library system.)
If we had two variables of type double
, d1
and d2
, we could say d1 = sqrt(d2);
In this case the value of d2
is passed to the sqrt
function. Or we could say d1 = sqrt(d2/3.7);
or d1 = sqrt(d2/3.7 * 5.4 + 8.913);
. The expression in the brackets (which is known as the argument) is evaluated, and its value is passed to the sqrt
function.
Using a function is known as calling a function; sqrt(d2)
is an example of a function call.
With square-roots, we were in luck; the C++ language designers had decided to include a square-root function in the standard library. But what if we need a function that is not provided in the library? Then we write our own.
Suppose we want a square function; square(x)
will return x * x
. You may feel that we hardly need a function for this since writing, say, double d2 = square(d1);
is no easier than writing double d2 = d1 * d1;
But suppose it's
double d3 = square(d1/3.706 * (d2/9.854 + 4.6));
That is, actually, easier, clearer and less error-prone than writing
double d3 = (d1/3.706 * (d2/9.854 + 4.6)) * (d1/3.706 * (d2/9.854 + 4.6));
So, as well as being a simple example, the square
function might actually be useful.
The first thing we have to decide, in writing the square
function, is what type of function it is, i.e. what type of thing it is going to return. It's clear from the examples above that the square
function is going to return a double
, so that is the first thing we write down when defining the function.
Next we have to think of a good name for it. I've already done that in calling it square
.
Now we have to decide what we are going to pass to it when we call it the things that it needs to be given (if any) to do its job. In the case of square
, this is simple; it needs a double
. We can now write the first line of the function definition:
double square(double dx)
This says, "We are defining a function called square
that takes a double
[that's the part in brackets] and also returns a double
[that's the first word on the line]."
The thing in brackets, that I have called dx
, is a parameter. It is declared like a variable a type name followed by an identifier and it behaves like a variable within the function.
Its purpose is to receive the value that gets passed when the function is called. I'll come back to that in a second. First let's complete the function definition.
We have written the first line, sometimes called the function header. Now we write the body of the function; this is where we specify what the function does. Since the purpose of a function is to return a value, we need to calculate the value to be returned. We do this in an ordinary block of code and return it with the keyword return
. (A block, you will recall, is some lines of program code between curly braces.) We could do it like this:
double square(double dx)
{ double lv;
lv = dx * dx;
return lv;
}
lv
is an example of a local variable i.e. a variable that is accessible only within the function. The parameter dx
also behaves like a local variable. The difference is that, whereas the value of the local variable lv
is initially undefined (unless we choose to initialize it), the parameter dx
will already have been given a value by the time this code executes.
Suppose that our function is called in the following way:
int main()
{ double d1 = 1.5;
cout << "The square of " << d1 << " is " << square(d1) << "." << endl;
}
When the computer executes the call square(d1)
it evaluates the argument, i.e. it works out that the current value of d1
is 1.5, and then passes this value to the function. This value is then used to initialize the parameter dx
. So, by the time the body of the function begins to execute, dx
already has the value 1.5. The function body then evaluates dx * dx
(i.e. multiplies 1.5 by 1.5), assigns the result (2.25) to be the value of lv
and then returns the value of lv
. So the value of the expression square(d1)
is 2.25, and the program's output is, "The square of 1.5 is 2.25."
The process by which a value is passed to the function and becomes the initial value of the parameter is known as parameter passing. This is the standard term though actually it is slightly odd since it's really the argument, or rather the value of the argument, that gets passed; the parameter is on the receiving end. When writing a function, bear in mind that, when your function code executes, the parameters will already have values. You don't have to do anything (for example with cin >>
) to put values into them; they already have values thanks to the parameter passing.
Note that the argument does not have to have the same name as the parameter. Equally, it would not matter if it did. The argument and the parameter are two different entities. It is the mechanism of parameter passing that links them, not their names.
Returning to our square
function, we can simplify it. The local variable is not really necessary, and we could write the function more simply as follows:
double square(double dx)
{ return dx * dx;
}
It only remains to say where the function definition goes in relation to the other parts of the program. For now, it will be simplest to put it after the using namespace std;
and before the int main()
. So, the whole program looks like this:
#include <iostream>
using namespace std;
double square(double dx)
{ return dx * dx;
}
int main()
{ double d1 = 1.5;
cout << "The square of " << d1 << " is " << square(d1) << "." << endl;
}
In this example, we are calling the function only once, but we could call it many times if we wanted to, and we could, if we wanted, give it a different argument each time. For example:
#include <iostream>
using namespace std;
double square(double dx)
{ return dx * dx;
}
int main()
{ double d1 = 1.5, d2 = 10.1;
cout << "The square of " << d1 << " is " << square(d1) << "." << endl;
cout << "The square of " << d2 << " is " << square(d2) << "." << endl;
cout << "The square of " << d1 + d2 << " is " << square(d1 + d2) << "." << endl;
cout << "The square of " << square(d1) << " is " << square(square(d1)) << "." << endl;
}
Write a function, called triplus
(just the function definition, not the call or any of the rest of the program) that takes an int
as its parameter and returns that number times three plus one. If the call was triplus(3)
it would return 10; for triplus(8)
it would return 25.
To check your answer, click on Answers to the exercises.
We can also have strings, both as parameters and as return types. The following is a function that takes a string as its parameter and returns the last character. For example, if place
was a string
with the value "Hornby", last(place)
would return the single-character string "y".
string last(string s)
{ return s.substr(s.length()-1); // or return s.substr(s.length()-1, 1);
}
Sometimes the function needs more than one thing to do its job. If, for example, we wanted a function that returned the longer of two strings (or either if they are the same length), it would have to have two parameters, and we would write them in a comma-separated list, each one with its data type:
string longer(string a, string b)
{ if (a.length() > b.length())
return a;
else return b;
}
The arguments, similarly, are presented in a comma-separated list:
string one = "Quernmore", two = "Abbeystead";
string three = longer(one, two);
The parameters do not all have to be of the same data type. For example, if we wanted a function that returned
the first n characters of a string, we would have to pass both the string and the value of n:
string firstpart(string s, int n)
{ return s.substr(0, n);
}
If our place
variable held the string "Ulverston", then firstpart(place, 5)
would return the value "Ulver". Note that the order of the arguments has to match the order of the parameters (in this case, a string
first followed by an int
).
Note also that you have to state the data type for each parameter in a parameter list, even if they are all the same. For example, a function that took three integers as its parameters and returned an integer would have a header something like this:
int func(int a, int b, int c)
In all the examples so far, the return type has been the same as the type of the parameters, or of one of the parameters, but the return type can be different. The following function, very like longer
above, returns the length of the longer string:
int longer_length(string a, string b)
{ if (a.length() > b.length())
return a.length();
else return b.length();
}
Change our last
function into a lastpart
function that returns the last n characters of a string. For example if place
held the value "Cartmel", lastpart(place, 3)
would return "mel". See if you can modify it further so that, if the value of n is longer than the string (such as lastpart(place, 10)
for "Cartmel") it returns the full string.
To check your answer, click on Answers to the exercises.
All the functions we have seen so far return a value. That is what they are for. But it is possible, in C++, to write a function that returns no value. You call such a function, not for what it returns (it returns nothing), but for what it does. Some other languages draw a distinction between functions (that return values) and procedures (that don't). In C++, both are called functions; it's just that some functions return no value. But I find it helpful to keep the term "procedure"; by "procedure" I simply mean "void function".
You begin the definition of one of these with the word void
as the return type. For example, we could write a procedure (void function) to take a string and to write it out with a few stars to left and right:
void stars(string s)
{ cout << "*** " << s << " ***" << endl;
}
We might call it as follows:
stars("Urgent");
and it would output the following line:
*** Urgent ***
A (non-void) function call is an expression, something that gets evaluated and returns a value, such as d2 = square(d1);
or if (sqrt(d) > 20.0)
or cout << lastpart(place, 3);
By contrast, a call to a void function is a statement, such as stars("Urgent");
This is how it might look in a program:
#include <iostream>
#include <string>
using namespace std;
void stars(string s)
{ cout << "*** " << s << " ***" << endl;
}
int main()
{ string message = "This is important.";
stars(message);
}
Note the difference between that and this alternative way of producing the same result:
#include <iostream>
#include <string>
using namespace std;
string starred(string s)
{ return "*** " + s + " ***";
}
int main()
{ string message = "This is important.";
cout << starred(message) << endl;
}
The same work is being done but it is divided between the parts of the program in a different way. In the first, main
simply hands over a message to stars
, and stars
both adds the stars and outputs it. In the second, main
asks starred
to return a new version of the message, with added stars, and then main
itself takes care of the outputting.
Which is better? For these tiny programs, it hardly matters. In the context of a larger program, it would depend on what else the program wanted to do with these starred messages. If it had various messages and sometimes output starred versions but sometimes did something else with a starred version, such as assigning it to be the value of another string:
string urgent_message = starred(message);
then the second would be better. But if the starring was only ever needed in conjunction with the outputting, the first might be simpler.
In the examples we have seen so far, it has always been main
that did the calling. But in fact it is possible for one function to call another. This enables us to have a third version:
#include <iostream>
#include <string>
using namespace std;
string starred(string s)
{ return "*** " + s + " ***";
}
void out_starred(string s)
{ cout << starred(s) << endl;
}
int main()
{ string message = "This is important.";
out_starred(message);
}
Given what we have covered so far, it would be necessary to lay out the program in that order the starred
function first, then the out_starred
procedure, and then main
.
Although procedures and functions generally have parameters, they don't have to. In other words, the parameter list can be empty. This procedure just outputs the string "Ouch!" whenever it is called:
void ouch()
{ cout << "Ouch!";
}
Note that you still need the brackets for the parameter list, even if there is nothing inside them.
You would call it thus:
ouch();
Note, also, that you still need the brackets for the argument list, even though there are no arguments.
The most obvious difference between a procedure definition and a (non-void) function definition is that the former begins with the word void
. But there is another. A (non-void) function always contains a return
whereas a procedure does not have to. If there is no return
in a procedure, it will simply execute and then the flow of program control will return to the line after the call. For example:
#include <iostream>
using namespace std;
// Assume that the first
and last
functions are defined here
void test_and_display(string s)
{ cout << "The string is " << s << endl;
cout << "The length is " << s.length() << endl;
cout << "The first character is " << first(s) << endl;
cout << "The last character is " << last(s) << endl;
// Control is returned from here.
}
int main()
{ string userstring;
cout << "Please key in a string: ";
cin >> userstring;
test_and_display(userstring);
// Control is returned to here.
cout << "Thank you and goodbye" << endl;
}
But you can have an explicit return
if you want. Of course, this is not like return dx * dx;
or return a.length();
because a void
function does not return anything. It is simply return;
You might use this if, in some circumstances, you did not want the procedure to execute in full. For example, the test_and_display
procedure would crash or produce peculiar output if it was given an empty string (a string of length zero, i.e. with no characters at all). We could prevent this by inserting some lines:
void test_and_display(string s)
{ if (s.length() == 0)
{ cout << "Empty string." << endl;
cout << "Zero length, no characters." << endl;
return; // Either control is returned from here
}
cout << "The string is " << s << endl;
....
// Or control is returned from here.
}
Or, if you preferred, you could achieve the same effect with an else
:
void test_and_display(string s)
{ if (s.length() == 0)
{ cout << "Empty string." << endl;
cout << "Zero length, no characters." << endl;
}
else
{ cout << "The string is " << s << endl;
....
}
// Control is returned from here.
}
Write a procedure (void function) that takes two integers and displays the sum, the difference (the larger minus the smaller), the product, the quotient (the larger divided by the smaller) and the remainder. For example, if given 13 and 5 (or 5 and 13), it would output the following:
The sum is 18.
The difference is 8.
The product is 65.
The quotient is 2, remainder 3.
Be careful not to divide by zero.
To check your answers, click on Answers to the exercises.
main
function
In most respects, main
is an ordinary function. As you can see from its header int main()
it has an empty parameter list and returns an int
. (Actually, it can have parameters, but that takes us beyond this introductory lesson.) But, if it's a function, where is the function call? Where does main
get called from? It gets called from the operating system, the program (Windows, Unix or whatever) that is running all the time a computer is switched on and which gives it its basic repertoire of behaviour maintaining the screen display, managing the file storage system and so on. When main
calls, for example, test_and_display
, it hands over control to the function and waits till it returns. In much the same way, when the operating system calls main
, it hands over control to your program and waits till main
returns, i.e. until your program terminates.
Since main
is an int
function, it returns an int
at the end, to the operating system. In some operating systems, Unix for example, it is possible to chain a series of programs together, each one passing on the results of its work to the next one. When one has finished, the operating system calls the next. In such a system, it is very useful for the individual programs to be able to signal to the operating system whether they were able to do their part successfully or whether something went wrong, in which case the whole chain of processing should be aborted. The integers returned from main
can be used for that. By convention, a return value of zero from main
means "Everything OK" and any other value means "Something went wrong." Since things can go wrong in different ways, it is possible to arrange for different return values to have different meanings.
It will not have escaped your notice that not one of the main
functions that we have had so far in these notes appears to return anything. Should we not be returning an int
since it's an int
function? We could, if we wanted, put a return 0;
as the last line of main
(if we were programming in C, rather than C++, it would be obligatory), but we don't have to. If we leave it out, the compiler puts it in for us. This is another respect in which main
differs from other functions. With all other (non-void) functions, an explicit return
is essential; with main
, you can leave the compiler to put it in for you.
Returning from main
allows us to terminate the program in a simple way. If something goes wrong such that there is no point in continuing the program (if, for example, we tried to read in a file of data but could not find it), we can terminate simply with, say, return 1;
(1 for "something's wrong" rather than 0 for "all's well"). Note, however, that it is only when we are in main
that we can terminate the program with a return
. If we are in any other function, a return
will simply terminate that particular function.
Write a function that takes a string as its parameter. If the string has fewer than three characters, it returns an empty string. Otherwise it returns the middle section of the string, i.e. the string minus its first and last characters. For example, if it was passed the string "Kendal", it would return "enda".
Write a procedure (void function) that takes a string as its parameter. If the string is empty, it does nothing. If the string has only one character, it outputs that character followed by a newline character. Otherwise it outputs the characters separated by spaces, with a newline on the end. For example, if it was passed the string "Carnforth", it would output:
C a r n f o r t h
Copying the firstpart
function (above) into your program in the right place, write a program that takes a string from the user and then, using a loop and using firstpart
, writes out a triangle of letters based on that string. For example, if the user keyed in the name "Morecambe", the computer would display the following:
M
Mo
Mor
More
Morec
Moreca
Morecam
Morecamb
Morecambe
To check your answers, click on Answers to the exercises.