Broad Network


Functions in C++

C++ Taking the Bull by the Horns – Part 15

Forward: A Function is a set of statements that perform a specific task. In this tutorial I explain functions in C++.

By: Chrysanthus Date Published: 22 Aug 2012

Introduction

This is part 15 of my series, C++ Taking the Bull by the Horns. A Function is a set of statements that perform a specific task. When you will get to writing programs, you will realize that programs are very long. You will also realize that there are groups of statements that will have to be doing the same task in different parts of the code (program). You do not need to type this group of statements in different parts of the code. You can type it once, and then call it wherever it is needed in the code. In this article I explain functions in C++.

Note: If you cannot see the code or if you think anything is missing (broken link, image absent), just contact me at forchatrans@yahoo.com. That is, contact me for the slightest problem you have about what you are reading.

As I said, I present C++ to you in this series the way the inventors see it. I do the presentation in simple terms. I believe that in this way you would understand C++ better. Remember, take things in this series as I give you. Do not try to add or subtract any idea in your mind to or from what I give you; that would be misleading. You can do any subtraction or addition after you complete the series.

Defining Functions
The group of statements forms the function, but you need to group them in a particular way. By doing this, we say you are defining a function. This process can be split in two phases. One phase is called, declaring the function and another phase is called, defining the function. For this tutorial we shall use a single process, which is defining the function. We shall see how to split the two processes later.

A function definition consists of the following in the order given

- The object returned type (see below)
- The name (identifier) of the function.
- A list of parameters to the function, enclosed in parentheses and separated by commas (see below).
- The statements that define the function are enclosed in curly braces. The statements in a function can have among them calls to other functions defined in the current program (application).

Example
In the following example, we define a function that will add two numbers, find the square of the sum and then print (display) the result.

#include <iostream>
using namespace std;

    void myFn()
        {
            int num1 = 2;
            int num2 = 3;

             int sum = num1 + num2;
            int square = sum * sum;

             cout << square;
        }

    int main()
        {
            myFn();

            return 0;
    }

The function begins with the word, void, then a space and myFn(), then a block of statements. There are actually two functions in the above code: one with the name, myFn and the other with the name, main. We shall say more about the main function later in the series. In this part of the tutorial we shall just say a few things about it.

Our function of interest, begins with the reserved word, void. The name of the function is myFn. This is followed by parentheses. Then you have the block. In the block, you have the definition and assignment of the two integer numbers. The third statement in the block sums the two numbers. The fourth statement squares the sum. The last statement displays (prints) the square.

Calling a Function
When the execution of a C++ program starts, all the statements in the main function block are executed first. In the above case, there are two statements in the block of the main function. I will talk about the second statement, “return 0;” later in some other part of the series. Execution of statements in any block begins from top to bottom.

In the execution of the statements in the block of the main function, the statement, “myFn()” will be executed. This statement calls the function, myFn, which is coded outside the block of the main function. In other words this statement causes the statements in the function, myFn, to be executed.

You call a function by just typing the name of the function, followed by parentheses, in a statement. The parentheses may have what is called arguments – see below. In the above code, if the function, myFn is never called, it will not be executed.

myFn is an example of a user defined function. The function you the user define should be coded outside the block of the main function.

The expression (e.g. myFn()) that calls a function is called the calling function. The function (definition) called, is called the called function.

The return value and return type
A function can return a value or pointer (address). Precisely, a function can return the content of an object or a pointer to an object. If a function returns a value or pointer, the calling expression e.g. myFn(), can be assigned to some identifier. You can then do whatever you want to do with the identifier. Consider the following code:

#include <iostream>
using namespace std;

    int myFn()
        {
            int num1 = 2;
            int num2 = 3;
            
             int sum = num1 + num2;
             int square = sum * sum;
            
            return square;
        }

int main()
    {
        int result = myFn();

        cout << result;

        return 0;
    }

In the myFn function definition this time, instead of having the cout object we have the return statement which is:

            return square;

A return statement begins with the reserved word, return, followed optionally by an expression. This expression can be just an identifier, e.g. square, as in the above case. All statements must end with a semicolon.

We know that in the myFn function, square is an object of type, int. Now, look inside the block of the main function. The right operand of the first statement is a function call (calling function) that calls the function (definition), myFn. This function call returns what was returned by the return statement in the function definition. It is the value of the object identified by square that was returned. In the block of the main function, this return value is assigned as content to the object, newly defined with the identifier, result. You can then use result in any way you want. The print statement in the block of the main function prints the value of result, which is the same value as that of square.

Now, if a function definition would return a value or a pointer, then you have to indicate that at the beginning of the function definition. In the first code sample, the function, myFn does not return anything, and because of that it does not have a return statement. So the function definition is begun with void. In the second code sample, the function, myFn, returns a value from an int object, so its definition is begun with int. You begin a function definition with the type of value of the object it will return. That is, you begin a function definition with the object type it will return. A function returns the value of an object, not the object itself.

Parameters and Arguments
Now, in the above function we can only deal with two particular numbers, which are 2 and 3. This is a disadvantage. If we define (create objects) and assign the identifiers outside the function, then we can always change the values of the objects (through their identifiers), then send the identifiers to the function before the function is executed. In this way, we shall be able to deal with many other pairs of numbers. The following example illustrates this:

#include <iostream>
using namespace std;

    int num1 = 2;
    int num2 = 3;

    int myFn(int no1, int no2)
        {
             int sum = no1 + no2;
             int square = sum * sum;
            
            return square;
        }

int main()
    {
        int result = myFn(num1, num2);

        cout << result;

        return 0;
    }

As said above any C++ program must have a main function. If you want to have any code segment outside the main function executed, then the code segment has to be called from the block of the main function.

This time the identifiers have been defined and assigned outside the function, myFn. Some other function elsewhere in the code can actually change these values. However, a function cannot change the value of an identifier inside some other function (everything being equal). In the definition of the function, the parentheses now have two object definitions. Each of these definitions begins with the type of object and then the identifier of the objects. The identifiers are for the two objects we need. These definitions in this position, in the parentheses of the function definition, are called Parameters. The parameters are separated by commas. The identifiers of these parameters are used within the function definition.

In the main function, where the function is called; the parentheses have two identifiers, without any preceding type (when calling a function, you do not need a preceding type). These identifiers in this position are called Arguments. The arguments are separated by commas. These arguments of the function are the identifiers defined outside the function, and simply have to be used without their preceding types. The arguments to a function call, can also be literals, something like:

    int result = myFn(4, 5);

Read the above code sample again and try it.

It is advisable to always make the identifiers for the parameters different from the corresponding identifiers for the arguments. If you do not do this, then while manipulating the parameters within the function, you might change the values of the identifiers outside the function.

Function and Pointers
For the rest of this part of the tutorial we shall see how pointers are used with functions. Strings are like special pointers and we shall look at strings last.

Function returning a Pointer
If a function will return a pointer, you precede the function name with * in the definition. In the block of such a function is a pointer object whose value (content address) is returned. The following code illustrates this:

#include <iostream>
using namespace std;

    int *theFn()
        {
            int pointed = 6;
            int *pointer = &pointed;
            
            return pointer;
        }

    int main()
        {
            int *receiver = theFn();
    
            cout << *receiver;
    
         return 0;
        }

The function, theFn, will return a pointer. So in the definition, the function name is preceded by the dereference operator, *. The type of value that the function will return precedes the *, which precedes the function name. The first statement in the block of the theFn function definition initializes an identifier that will be used as the pointed object. The second statement initializes a pointer object for the above identifier. The last statement returns the pointer (address).

The first statement in the main function is:

        int *receiver = theFn();

This statement initializes the pointer, receiver. The right operand is a call to the function that returns a pointer (memory address). It is normal to initialize a pointer using the memory address of a pointed object. So in this initialization statement, a pointer (memory address) goes into the object identified by, receiver. So far as the whole code is concerned, two pointer objects now have the memory address of the pointed object: the pointer objects are pointer in the theFn function block and receiver in the main function block.

Now, two pointers are pointing to the same object. In order to get the value of the object they are pointing to, you have to use the dereference operator on any of the pointers in their deferent blocks. The cout Object uses the dereferece operator, * on the pointer, receiver.

Note that when calling the function, we did not precede the function name, theFn, with *. Try the above code.

Passing Pointer to a Function
A parameter of a function may be a pointer type. The following code illustrates its use:

#include <iostream>
using namespace std;

    int aFn(int *no)
        {
            cout << *no; cout << "\n";

            int anInt = 77;
            no = &anInt;
            
            return *no;
        }

    int main()
        {
            int myInt = 88;
            int hisRet = aFn(&myInt);
    
            cout << hisRet;
    
            return 0;
        }

When the program starts the statements in the main function are executed. The first statement in the main function creates an int object whose identifier is myInt. The value of 88 is assigned to it. For the next statement, the right operand calls the function, aFn. This function returns an integer, that is assigned to an int object whose identifier is hisRet (in the main function). The argument to this function call is the address (pointer) of the object identified by myInt (in the main function). The value of this object whose address is sent as argument is 88.

The function (definition), aFn is called, by the right operand of the second statement in the main function. Outside the main function, the parameter of the aFn function is,

        int *no

The identifier, no, is used in the function definition. Just before the statements in the aFn function definition are executed, the following initialization takes place unknown to you:

    int *no = &myInt;

The right operand of this initialization is the address of an object initialized in the main function. This address was sent as argument in the function call. The left operand, here, has the pointer definition of the parameter of the function definition. In the initialization of a pointer, the right operand is an address even though the left operand means Value Of. So this initialization is OK. The statements in the function definition use this hidden initialization.

The first line in the aFn function definition prints the value pointed to by no; that is the first line prints *no. From the hidden initialization, this value is 88. The second statement in the definition initializes a new int identifier, anInt, with the value 77. The third statement copies the address of this object identified by anInt, to become content of the object identified by no. no now pointes to an object whose value is 77 instead of the object whose value is 88. The fourth statement in the definition returns the new value pointed to by no, that is *no, which is now 77.

Back to the main function: When the program starts, the statements in the main function are first executed. The first statement in the main function assigns the value 88 to the object identified by myInt. The second statement in the main function calls the function, aFn. The third statement in main will only execute after this called function has finished executing. When the execution of the called function, aFn completes, the function aFn returns the value 77. This value is assigned to the int object identified by hisRet in the main function. After this, execution of the statements in the main function continues.

Next is the cout statement (third statement) in the main function. This prints the value of hisRet, which is 77. I will comment about the last statement, “return 0;” later.

Passing Reference to a Function
The parameter of a function can be the address (reference) of an object. The following code illustrates this:

#include <iostream>
using namespace std;

    int aFn(int &no)
        {
            cout << no; cout << "\n";

            no = 77;
            
        }

    int main()
        {
            int theInt = 88;
            aFn(theInt);
    
            cout << theInt;
    
            return 0;
        }

As the program starts, 88 is assigned to the object whose identifier is theInt. The next statement in the main function calls the function, aFn, passing the identifier of the object whose value is 88 as argument. The parameter of the called function is “int &no”. Immediately the called function starts executing, the parameter address becomes equivalent to &theInt, which is the address of the object defined in the first statement in the main function. So immediately the execution of the called function starts, &no and &theInt are the same address to one object; that is, no and theInt are identifying the same object.

The first statement in the called function displays the value of no which is the same as the value of theInt. The second statement in the called function assigns a new value to no, automatically assigning the new value to theInt, because both identifiers identify the same object. The called function does not have a return statement even though its definition begins with a return type. That is acceptable.

Back in the main function: The called function is called by the second statement in the main function. The called function executes before the next (third) statement in the main function is executed. We did not assign any return value of the called function to any object (identifier) in the main function. We could not do this because the called function did not return anything (had no return statement).

The third statement in the main function displays the value of theInt, which is the same value as that for no in the called function.

Note, when passing by reference, the parameter (called function) is the address of an object (identifier) but the argument (calling function) is an identifier and not the address of an object (identifier).

Note: When passing a pointer to a function, a new object (such as, no in the previous code) is created and initialization takes place between the parameter (called function) and the argument (calling function). In the case of passing a reference, both the identifier (such as, no above) of the parameter and the identifier of the argument, identify the same object; no new object is created.

Default Value in Parameter
Imagine that you want a function to be adding two values. One can be changing, but the other one should not change. The one that cannot change is called the default value and it can be coded in the parameter list of the function. Read and try the following code that illustrates this:

#include <iostream>
using namespace std;

    void fn(int p, int q=20)
        {
            int w = p + q;

            cout << w;
        }

    int main()
        {
            int p = 15;
            fn(p);
            
            return 0;
        }

You use an initialization for the default value in the parameter (do not follow that with a semicolon). If you do not like the value for q, then in the function call, you should send a second argument for q. Note that in the function call (calling function) the argument for the default value is not sent; that is, there is no argument for the default value.

Passing String to a Function
A parameter of string type has to be a pointer to char. The argument for the calling function can be a string literal or a pointer (address) to the beginning of some string in memory. The following two code samples illustrate this (if you try them, ignore the warnings for now).

#include <iostream>
using namespace std;

    int strFn(char *str)
        {
            cout << str;
        }

    int main()
        {
            strFn("a test");

            return 0;
        }

The second string code sample is as follows:

#include <iostream>
using namespace std;

    int strFn(char *str)
        {
            cout << str;
        }

    int main()
        {
            char *myStr = "a test";
            strFn(myStr);
    
            return 0;
        }

In the first code sample, just before the strFn function (definition) is executed, the following initialization takes place unknown to you:

        char *str = "a test";

In the second code sample, just before the strFn function (definition) is executed, the following initialization takes place unknown to you:

            char *str = myStr;

To appreciate the above initialization, note that the following is true for any pointer type:

        Type *pointer1;
        Type *pointer2 = pointer1;

The following is also true:

        Type *pointer1;
        Type *pointer2;

        pointer1 = pointer2;

You can still assign &ident to any of the pointers, where ident is the identifier of some object. myStr returns the address of the first character of the string array in memory.

We have seen a lot. We have to take a break. We continue in the next part of the series.

Chrys

Related Courses

C++ Course
Relational Database and Sybase
Windows User Interface
Computer Programmer – A Jack of all Trade – Poem
NEXT

Comments

Become the Writer's Fan
Send the Writer a Message