Mastering C++ Function Essentials

C++C++Beginner
Practice Now

Introduction

In this lab, you will learn the functions in C++. You will learn how to define and call functions, and how to pass arguments to functions.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL cpp(("`C++`")) -.-> cpp/SyntaxandStyleGroup(["`Syntax and Style`"]) cpp(("`C++`")) -.-> cpp/BasicsGroup(["`Basics`"]) cpp(("`C++`")) -.-> cpp/ControlFlowGroup(["`Control Flow`"]) cpp(("`C++`")) -.-> cpp/FunctionsGroup(["`Functions`"]) cpp/SyntaxandStyleGroup -.-> cpp/comments("`Comments`") cpp/BasicsGroup -.-> cpp/variables("`Variables`") cpp/BasicsGroup -.-> cpp/data_types("`Data Types`") cpp/BasicsGroup -.-> cpp/operators("`Operators`") cpp/ControlFlowGroup -.-> cpp/conditions("`Conditions`") cpp/ControlFlowGroup -.-> cpp/for_loop("`For Loop`") cpp/BasicsGroup -.-> cpp/arrays("`Arrays`") cpp/FunctionsGroup -.-> cpp/function_parameters("`Function Parameters`") subgraph Lab Skills cpp/comments -.-> lab-178537{{"`Mastering C++ Function Essentials`"}} cpp/variables -.-> lab-178537{{"`Mastering C++ Function Essentials`"}} cpp/data_types -.-> lab-178537{{"`Mastering C++ Function Essentials`"}} cpp/operators -.-> lab-178537{{"`Mastering C++ Function Essentials`"}} cpp/conditions -.-> lab-178537{{"`Mastering C++ Function Essentials`"}} cpp/for_loop -.-> lab-178537{{"`Mastering C++ Function Essentials`"}} cpp/arrays -.-> lab-178537{{"`Mastering C++ Function Essentials`"}} cpp/function_parameters -.-> lab-178537{{"`Mastering C++ Function Essentials`"}} end

Content Preview

At times, a certain portion of codes has to be used many times. It is better to put them into a "subroutine"--function, and "call" this function many times - for ease of maintenance and understanding.

Two parties are involved in using a function: a caller who calls the function, and the function called. The caller passes argument(s) to the function. The function receives these argument(s), performs the programmed operations within the function's body, and returns a piece of result back to the caller.

Using Functions

Suppose that we need to evaluate the area of a circle many times, it is better to write a function called getArea(), and re-use it when needed.

/* Test Function */
#include <iostream>
using namespace std;
const int PI = 3.14159265;

// Function Prototype (Function Declaration)
double getArea(double radius);

int main() {
   double radius1 = 1.1, area1, area2;
   // call function getArea()
   area1 = getArea(radius1);
   cout << "area 1 is " << area1 << endl;
   // call function getArea()
   area2 = getArea(2.2);
   cout << "area 2 is " << area2 << endl;
   // call function getArea()
   cout << "area 3 is " << getArea(3.3) << endl;
}

// Function Definition
// Return the area of a circle given its radius
double getArea(double radius) {
   return radius * radius * PI;
}

Output:

area 1 is 3.63
area 2 is 14.52
area 3 is 32.67
image desc
image desc

In C++, you need to declare a function prototype (before the function is used), and provide a function definition, with a body containing the programmed operations.

The syntax for function definition is as follows:

returnValueType functionName ( parameterList ) {
   functionBody ;
}

A function prototype tells the compiler the function's interface, i.e., the return-type, function name, and the parameter type list (the number and type of parameters). The function can now be defined anywhere in the file. For example,

// Function prototype - placed before the function is used.
double getArea(double);  // without the parameter name
double getArea(double radius);  // parameter names are ignored, but serve as documentation

The "void" Return Type

Without a need to return a value to the caller, you can declare its return-value type as void. In the function's body, you could use a "return;" statement without a return value to return control to the caller.

Actual Parameters vs. Formal Parameters

In the above example, the variable (double radius) declared in the signature of getArea(double radius) is known as formal parameter. Its scope is within the function's body. When the function is invoked by a caller, the caller must supply so-called actual parameters (or arguments), whose value is then used for the actual computation. For example, when the function is invoked via "area1 = getArea(radius1)", radius1 is the actual parameter, with a value of 1.1.

Scope of Function's Local Variables and Parameters

All variables, including function's parameters, declared inside a function are available only to the function. They are created when the function is called, and freed (destroyed) after the function returns. They are called local variables because they are local to the function and not available outside the function.

Default Arguments

C++ introduces so-called default arguments for functions. These default values would be used if the caller omits the corresponding actual argument in calling the function. Default arguments are specified in the function prototype, and cannot be repeated in the function definition. The default arguments are resolved based on their positions. Hence, they can only be used to substitute the trailing arguments to avoid ambiguity. For example,

/* Test Function default arguments */
#include <iostream>
using namespace std;

// Function prototype - Specify the default arguments here
int fun1(int = 1, int = 2, int = 3);
int fun2(int, int, int = 3);

int main() {
   cout << fun1(4, 5, 6) << endl; // No default
   cout << fun1(4, 5) << endl;    // 4, 5, 3(default)
   cout << fun1(4) << endl;       // 4, 2(default), 3(default)
   cout << fun1() << endl;        // 1(default), 2(default), 3(default)

   cout << fun2(4, 5, 6) << endl; // No default
   cout << fun2(4, 5) << endl;    // 4, 5, 3(default)
   // cout << fun2(4) << endl;
   // error: too few arguments to function 'int fun2(int, int, int)'
}

int fun1(int n1, int n2, int n3) {
   // cannot repeat default arguments in function definition
   return n1 + n2 + n3;
}

int fun2(int n1, int n2, int n3) {
   return n1 + n2 + n3;
}

Output:

15
12
9
6
15
12
image desc

Function Overloading

C++ introduces function overloading (or function polymorphism), which allows you to have multiple versions of the same function name, differentiated by the parameter list (number, type or order of parameters). Overloaded functions cannot be differentiated by the return-type (compilation error). The version matches the caller's argument list will be selected for execution. For example,

/* Test Function Overloading */
#include <iostream>
using namespace std;

void fun(int, int, int);  // Version 1
void fun(double, int);          // Version 2
void fun(int, double);          // Version 3

int main() {
   fun(1, 2, 3);   // version 1
   fun(1.0, 2);    // version 2
   fun(1, 2.0);    // version 3
   fun(1.1, 2, 3); // version 1 - double 1.1 casted to int 1 (without warning)

   // fun(1, 2, 3, 4);
      // error: no matching function for call to 'fun(int, int, int, int)'
   // fun(1, 2);
      // error: call of overloaded 'fun(int, int)' is ambiguous
      // note: candidates are:
      //    void fun(double, int)
      //    void fun(int, double)
   // fun(1.0, 2.0);
      // error: call of overloaded 'fun(double, double)' is ambiguous
}

void fun(int n1, int n2, int n3) {  // version 1
   cout << "version 1" << endl;
}

void fun(double n1, int n2) { // version 2
   cout << "version 2" << endl;
}

void fun(int n1, double n2) { // version 3
   cout << "version 3" << endl;
}

Output:

version 1
version 2
version 3
version 1
image desc

Functions and Arrays

You can also pass arrays into function. However, you also need to pass the size of the array into the function. This is because there is no way to tell the size of the array from the array argument inside the called function. For example,

/* Function to compute the sum of an array */
#include <iostream>
using namespace std;

// Function prototype
int sum(int array[], int size);    // Need to pass the array size too
void print(int array[], int size);

// Test Driver
int main() {
   int a1[] = {8, 4, 5, 3, 2};
   print(a1, 5);   // {8,4,5,3,2}
   cout << "sum is " << sum(a1, 5) << endl;  // sum is 22
}

// Function definition
// Return the sum of the given array
int sum(int array[], int size) {
   int sum = 0;
   for (int i = 0; i < size; ++i) {
      sum += array[i];
   }
   return sum;
}

// Print the contents of the given array
void print(int array[], int size) {
   cout << "{";
   for (int i = 0; i < size; ++i) {
      cout << array[i];
      if (i < size - 1) {
         cout << ",";
      }
   }
   cout << "}" << endl;
}

Output:

{8,4,5,3,2}
sum is 22
image desc

Pass-by-Value vs. Pass-by-Reference

There are two ways that a parameter can be passed into a function: pass by value vs. pass by reference.

Pass-by-Value

In pass-by-value, a "copy" of argument is created and passed into the function. The invoked function works on the "clone", and cannot modify the original copy. In C/C++, fundamental types (such as int and double) are passed by value.

/* Fundamental types are passed by value into Function */
#include <iostream>
using namespace std;

// Function prototypes
int inc(int number);

// Test Driver
int main() {
   int n = 8;
   cout << "Before calling function, n is " << n << endl; // 8
   int result = inc(n);
   cout << "After calling function, n is " << n << endl;  // 8
   cout << "result is " << result << endl;                // 9
}

// Function definitions
// Return number+1
int inc(int number) {
   ++number;  // Modify parameter, no effect to caller
   return number;
}

Output:

Before calling function, n is 8
After calling function, n is 8
result is 9
image desc

Pass-by-Reference

On the other hand, in pass-by-reference, a reference of the caller's variable is passed into the function. In other words, the invoked function works on the same data. If the invoked function modifies the parameter, the same caller's copy will be modified as well. In C/C++, arrays are passed by reference. C/C++ does not allow functions to return an array.

/* Function to increment each element of an array */
#include <iostream>
using namespace std;

// Function prototypes
void inc(int array[], int size);
void print(int array[], int size);

// Test Driver
int main() {
   int a1[] = {8, 4, 5, 3, 2};

   // Before increment
   print(a1, 5);   // {8,4,5,3,2}
   // Do increment
   inc(a1, 5);     // Array is passed by reference (having side effect)
   // After increment
   print(a1, 5);   // {9,5,6,4,3}
}

// Function definitions

// Increment each element of the given array
void inc(int array[], int size) {  // array[] is not const
   for (int i = 0; i < size; ++i) {
      array[i]++;  // side-effect
   }
}

// Print the contents of the given array
void print(int array[], int size) {
   cout << "{";
   for (int i = 0; i < size; ++i) {
      cout << array[i];
      if (i < size - 1) {
         cout << ",";
      }
   }
   cout << "}" << endl;
}

Output:

{8,4,5,3,2}
{9,5,6,4,3}
image desc

Const Function Parameters

Use const whenever possible for passing references as it prevent you from inadvertently modifying the parameters and protects you against many programming errors.

In a linear search, the search key is compared with each element of the array linearly. If there is a match, it returns the index of matched element; otherwise, it returns -1. Linear search has complexity of O(n).

/* Search an array for the given key using Linear Search */
#include <iostream>
using namespace std;

int linearSearch(const int a[], int size, int key);

int main() {
   const int SIZE = 8;
   int a1[SIZE] = {8, 4, 5, 3, 2, 9, 4, 1};

   cout << linearSearch(a1, SIZE, 8) << endl;  // 0
   cout << linearSearch(a1, SIZE, 4) << endl;  // 1
   cout << linearSearch(a1, SIZE, 99) << endl; // 8 (not found)
}

// Search the array for the given key
// If found, return array index [0, size-1]; otherwise, return size
int linearSearch(const int a[], int size, int key) {
   for (int i = 0; i < size; ++i) {
      if (a[i] == key) return i;
   }
   // a[0] = 1;
   // It will result in an error,because a[] is const, means read only
   return -1;
}

Output:

0
1
-1
image desc

Pass-by-Reference via "Reference" Parameters

You can pass a fundamental type parameter by reference via the reference parameter denoted by &.

/* Test Pass-by-reference for fundamental-type parameter
   via reference declaration */
#include <iostream>
using namespace std;

int squareByValue (int number);        // Pass-by-value
void squareByReference (int &number); // Pass-by-reference

int main() {
   int n1 = 8;
   cout << "Before call, value is " << n1 << endl;  // 8
   cout << squareByValue(n1) << endl;  // no side-effect
   cout << "After call, value is " << n1 << endl;   // 8

   int n2 = 9;
   cout << "Before call, value is " << n2 << endl;  // 9
   squareByReference(n2);  // side-effect
   cout << "After call, value is " << n2 << endl;   // 81
}

// Pass parameter by value - no side effect
int squareByValue (int number) {
   return number * number;
}

// Pass parameter by reference by declaring as reference (&)
// - with side effect to the caller
void squareByReference (int &number) {
   number = number * number;
}

Output:

Before call, value is 8
64
After call, value is 8
Before call, value is 9
After call, value is 81
image desc

###2.8 Math Functions

C++ provides many common-used Mathematical functions in library <cmath>

sin(x), cos(x), tan(x), asin(x), acos(x), atan(x):
   Take argument-type and return-type of float, double, long double.
sinh(x), cosh(x), tanh(x):
   hyper-trigonometric functions.
pow(x, y), sqrt(x):
   power and square root.
ceil(x), floor(x):
   returns the ceiling and floor integer of floating point number.
fabs(x), fmod(x, y):
   floating-point absolute and modulus.
exp(x), log(x), log10(x):
   exponent and logarithm functions.

The cstdlib header (ported from C's stdlib.h) provides a function rand(), which generates a pseudo-random integral number between 0 and RAND_MAX (inclusive).

/* Test Random Number Generation */
#include <iostream>
#include <cstdlib>  // for rand(), srand()
#include <ctime>    // for time()
using namespace std;

int main() {
   // rand() generate a random number in [0, RAND_MAX]
   cout << "RAND_MAX is " << RAND_MAX << endl;   // 32767

   // Generate 10 pseudo-random numbers between 0 and 99
   //   without seeding the generator.
   // You will get the same sequence, every time you run this program
   for (int i = 0; i < 10; ++i) {
      cout << rand() % 100 << " ";   // need <cstdlib> header
   }
   cout << endl;

   // Seed the random number generator with current time
   srand(time(0));   // need <cstdlib> and <ctime> header
   // Generate 10 pseudo-random numbers
   // You will get different sequence on different run,
   //   because the current time is different
   for (int i = 0; i < 10; ++i) {
      cout << rand() % 100 << " ";   // need <cstdlib> header
   }
   cout << endl;
}

Output:

RAND_MAX is 2147483647
83 86 77 15 93 35 86 92 49 21
29 0 83 60 22 55 97 80 68 87
image desc
- name: check if keyword exist
  script: |
    #!/bin/bash
    grep -i 'rand' /home/labex/Code/test.cpp
  error: Oops! We find that you didn't use "rand()" method in "test.cpp".
  timeout: 3

Summary

The benefits of using functions are:

  1. Divide and conquer: construct the program from simple, small pieces or components. Modularize the program into self-contained tasks.
  2. Avoid repeating codes: It is easy to copy and paste, but hard to maintain and synchronize all the copies.
  3. Software Reuse: you can reuse the functions in other programs, by packaging them into library codes.

Other C++ Tutorials you may like