Functions

Functions.

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:

Function call blackbox abstraction figure

Example: Uppercase input

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!

Some functions from the C++ Standard Library.

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 directive

If 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.

Read more.

Mathematical function from <cmath> and <cstdlib>

There are tons, the table above shows just a few.

Pseudo-random number generation (PRNG)

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).

User-defined functions

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;
}

Example: The greatest common divisor (a simple algorithm)

#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;
}

Example: Integer square root

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>.