A function (or a subroutine) is a sequence of program instructions that perform a specific task, packaged as a unit. This unit can then be used in programs wherever that particular task should be performed.
What are the advantages of using functions? They let us:
sqrt(x)
is easier to understand than 20 lines of code)
To get an idea, how functions can be used to make your code more readable, please consider the following example. We are going to take a program and rewrite it using a function.
The exact syntax of the function definition will be discussed in the next lecture. Don’t worry about these details at the moment.
The original program. The following program asks the user to enter a characted. Then it prints the character uppercased (if the input was a lowercase letter).
#include <iostream>
using namespace std;
int main() {
cout << "Enter a character: ";
char c = ' ';
cin >> c;
if ('a' <= c && c <= 'z') {
// if it's a lowercase letter, uppercase it
char uppercase_c = c - 32; // see ASCII table
cout << uppercase_c << endl;
}
else
// otherwise print the original character
cout << c << endl;
return 0;
}
Rewrite.
We can define a new function uppercase
and use it in place of that if
statement:
#include <iostream>
using namespace std;
/* uppercases letters 'a' - 'z' */
char uppercase(char c) {
if ('a' <= c && c <= 'z')
return (c - 32); // see ASCII table
else
return c;
}
int main() {
cout << "Enter a character: ";
char c = ' ';
cin >> c;
cout << uppercase(c) << endl;
return 0;
}
The implementation details of the function uppercase
are abstracted away
and packaged into a single block of code that can be now used in any place of our program!
Function | Description | Include | Arguments | Returns |
---|---|---|---|---|
sqrt(x) |
Square root | <cmath> |
double |
double |
pow(x,y) |
Power, xy | <cmath> |
double |
double |
fabs(x) |
Absolute value, |x| | <cmath> |
double |
double |
ceil(x) |
Ceiling (round up) | <cmath> |
double |
double |
floor(x) |
Floor (round down) | <cmath> |
double |
double |
abs(x) |
Absolute value, |x| | <cstdlib> |
int |
int |
labs(x) |
Absolute value, |x| | <cstdlib> |
long |
long |
rand() |
Random number | <cstdlib> |
void |
int |
srand(x) |
Set seed for rand | <cstdlib> |
int |
void |
exit(x) |
Exit with status x | <cstdlib> |
int |
void |
#include
preprocessor directiveIf you want to use a function declared in the header <cmath>
, add a line
#include <cmath>
This preprocessor directive inserts the header file “cmath” in the body of the program before it’s compiled.
If you are curious to see this header file, on Linux systems it can be found in the folder /usr/lib/c++
(or similar).
We will learn more about header files later, when will be talking about separate compilation.
<cmath>
and <cstdlib>
There are tons, the table above shows just a few.
Video: Random vs Pseudo-random
Note that the functions rand
and srand
are old C functions, and they don’t really offer a lot:
You can only generate numbers between 0
and RAND_MAX
(inclusive).
The new C++11 standard provides a much more feature-complete pseudo-random number generation module:
for example, you can choose a PRNG engine,
and you can sample from different mathematical distributions (e.g. Uniform, Normal, Binomial, Poisson, and many more)
#include <iostream>
#include <cstdlib>
using namespace std;
int main() {
// If you need random numbers that differ each time you run
// the program, the standard practice is to set the seed
// equal to the currect system time
int seed = time(NULL);
// The value returned represents the number of seconds since
// 00:00 hours, Jan 1, 1970 UTC (i.e., the current unix timestamp).
cout << "seed = " << seed << endl << endl;
srand(seed); // set the seed
cout << rand() << endl;
cout << rand() << endl;
cout << rand() << endl;
cout << rand() << endl;
cout << rand() << endl << endl;
cout << "RAND_MAX = " << RAND_MAX << endl << endl;
// Generating bounded integers, for example 0 .. 99:
cout << rand() % 100 << endl;
cout << rand() % 100 << endl;
cout << rand() % 100 << endl;
cout << rand() % 100 << endl;
cout << rand() % 100 << endl << endl;
// Generatin random floating-point values between 0 and 1:
cout << static_cast<double>(rand()) / RAND_MAX << endl;
cout << static_cast<double>(rand()) / RAND_MAX << endl;
cout << static_cast<double>(rand()) / RAND_MAX << endl;
cout << static_cast<double>(rand()) / RAND_MAX << endl;
cout << static_cast<double>(rand()) / RAND_MAX << endl;
}
seed = 1423519433
595181471
1915328273
827486926
251559006
989564762
RAND_MAX = 2147483647
98
25
27
28
84
0.106128
0.718199
0.926017
0.682871
0.14805
exit(1)
Function exit(x)
exits the program. Its argument is called the exit status, this is the number reported to the operating system
when the program terminates.
The argument (exit status) 1
means that the program terminates because of an error.
And the argument 0
would mean that the program terminates successfully.
The return 0;
statement at the end of the main
function serves the same purpose there, reporting the status code 0
(i.e. success)
to the operating system.
So, if you have to terminate your program in the middle of execution due to some unexpected failure, you may call exit(1)
.
An example of a user-defined function:
// the function rounds a value of type double and returns int
int round (double x) {
int result = static_cast<int>( floor(x + 0.5) );
return result;
}
For the reasons we will explain later, it’s common to first declare all the functions at the beginning of the file,
and then define them at the end of the file, after the main
function
#include <cmath>
// the function rounds a value of type double and returns int
int round (double x); // <-- declaration
int main() {
...
return 0;
}
int round (double x) { // <-- definition
int result = static_cast<int>( floor(x + 0.5) );
return result;
}
#include <iostream>
using namespace std;
/* GCD, the greatest common divisor */
int gcd(int x, int y);
int main() {
int a = 0, b = 0;
cout << "a = "; cin >> a;
cout << "b = "; cin >> b;
cout << "The GCD is " << gcd(a, b) << endl;
return 0;
}
int gcd(int x, int y) {
int div = 1;
int largest = 1;
while ( div <= x ) {
if (x % div == 0 && y % div == 0) {
largest = div;
}
div++;
}
return largest;
}
Accoding to the definition, the integer square root of a positive integer n is the positive integer m which is the greatest integer less than or equal to the square root of n.
#include <iostream>
using namespace std;
/* integer square root */
int isqrt(int n);
int main() {
cout << "Enter a positive integer: ";
int n = 0;
cin >> n;
cout << "Integer square root is " << isqrt(n) << endl;
return 0;
}
int isqrt(int n) {
int x = 0;
while ( x*x <= n ) {
x++;
}
return (x - 1);
}
Observe that the same result could be achieved if we defined the function as follows:
int isqrt(int n) {
return static_cast<int>( floor(sqrt(n)) );
}
Of course, in this case we are using the library functions sqrt
and floor
, and so need to include the header <cmath>
.