C++

How to use C++ Pointers

The memory of a computer is actually a long series of cells. The size of each cell is called a byte. A byte is a space occupied by an English character of the alphabet. An object in the ordinary sense is a consecutive set of bytes in memory. Each cell has an address, which is an integer, usually written in hexadecimal form. There are three ways of accessing an object in memory. An object can be accessed using what is known as a pointer. It can be accessed using what is known as a reference. It can still be accessed using an identifier. The focus of this article is on the use of pointers and references. In C++, there is the pointed object and the pointer object. The pointed object has the object of interest. The pointer object has the address to the pointed object.

You need to have basic knowledge in C++, including its identifiers, functions, and arrays; to understand this article.

The pointer object and the pointed object, each has its own identifier.

The Address-Of Operator, &

This is a unary operator. When followed by an identifier, it returns the address of the object of the identifier. Consider the following declaration:

int ptdInt;

Below in the code, the following expression, will return the address identified by ptdInt:

&ptdInt

You do not need to know the exact address (number) as you code.

The Indirection Operator, *

This is a unary operator in the context of pointers. It is usually typed in front of an identifier. If used in a declaration of the identifier, then the identifier is the pointer object which holds only the address of the pointed object. If used in front of the pointer object identifier, to return something, then the thing returned is the value of the pointed object.

Creating a Pointer

Take a look at the following code segment:

float ptdFloat;
float *ptrFloat;

ptrFoat = &ptdFloat;

The segment begins with the declaration of the pointed object, ptdFloat. ptdFloat is an identifier, which just identifies a float object. An actual object (value) could have been assigned to it, but in this case, nothing has been assigned to it. Next in the segment, there is the declaration of the pointer object. The indirection operator in front of this identifier means it has to hold the address of a pointed object. The object type, float at the beginning of the statement, means the pointed object is a float. The pointer object is always of the same type as the pointed object. ptrFoat is an identifier, which just identifies a pointer object.

In the last statement of the code, the address of the pointed object is assigned to the pointer object. Note the use of the address-of operator, &.

The last statement (line) above shows, that after declaring the pointer object without initialization, you do not need the indirection operator, when you have to initialize it. In fact, it is a syntax error to use the indirection operator in the third (last) line.

The pointer object can be declared and initialized by the pointed object in one statement, as follows:

float ptdFloat;
float *ptrFoat = &ptdFloat;

The first line of the previous code segment and this one, are the same. The second and third lines of the previous code segment have been combined into one statement here.

Note in the above code that when declaring and initializing the pointer object, the indirection operator has to be used. However, it is not used if the initialization is to be done afterward. The pointer object is initialized with the address of the pointed object.

In the following code segment, the indirection operator is used to return the content of the pointed object.

int ptdInt = 5;
int *ptrInt = &ptdInt;

cout << *ptrInt << '\n';

The output is 5.

In the last statement here, the indirection operator has been used to return the value pointed to, by the pointer identifier. So, when used in a declaration, the identifier for the indirection operator would hold the address of the pointed object. When used in a return expression, in combination with the pointer identifier, the indirection operator returns the value of the pointed object.

Assigning Zero to a Pointer

The pointer object should always have the type of the pointed object. When declaring the pointer object, the data type of the pointed object has to be used. However, the value of decimal zero can be assigned to the pointer as in the following code segment:

int ptdInt = 5;
int *ptrInt;

ptrInt = 0;

or in the segment,

int ptdInt = 5;
int *ptrInt = 0;

In either case, the pointer (identifier) is called the null pointer; meaning, it points to nowhere. That is, it does not have the address of any pointed object. Here, 0 is decimal zero and not hexadecimal zero. Hexadecimal zero would point to the first address of the computer memory.

Do not try to obtain the value pointed to by a null pointer. If you try that, the program may compile, but may not execute.

Array Name as a Constant Pointer

Consider the following array:

int arr[] = {000, 100, 200, 300, 400};

The name of the array, arr is actually the identifier that has the address of the first element of the array. The following expression returns the first value in the array:

*arr

With the array, the increment operator, ++ behaves differently. Instead of adding 1, it replaces the address of the pointer, with the address of the next element in the array. However, the name of the array is a constant pointer; meaning its content (address) cannot be changed or incremented. So, to increment, the start address of the array has to be assigned to a non-constant pointer as follows:

int *ptr = arr;

Now, ptr can be incremented to point to the next element of the array. ptr has been declared here as a pointer object. Without * here, it would not be a pointer; it would be an identifier to hold an int object and not to hold a memory address.

The following code segment finally points to the fourth element:

++ptr;
++ptr;
++ptr;

The following code outputs the fourth value of the array:

int arr[] = {000, 100, 200, 300, 400};

int *ptr = arr;

++ptr;
++ptr;
++ptr;

cout << *ptr << '\n';

The output is 300.

Function Name as an Identifier

The name of a function is the identifier of the function. Consider the following function definition:

int fn()
    {
        cout << "seen" << '\n';

        return 4;
    }

fn is the identifier of the function. The expression,

&fn

returns the address of the function in memory. fn is like the pointed object. The following declaration declares a pointer to a function:

int (*func)();

The identifier for the pointed object and the identifier for the pointer object is different. func is a pointer to a function. fn is the identifier of a function. And so, func can be made to point to fn as follows:

func = &fn;

The value (content) of func is the address of fn. The two identifiers could have been linked with an initialization statement as follows:

int (*func)() = &fn;

Note the differences and similarities in handling function pointers and scalar pointers. func is a pointer to a function; it is the pointed object; it is declared differently from a scalar pointer.

The function can be called with,

fn()

or

func()

It cannot be called with *func().

When the function has parameters, the second parentheses have the types of the parameters and does not need to have the identifiers for the parameters. The following program illustrates this:

#include <iostream>
using namespace std;

float fn(float fl, int in)
    {
        return fl;
    }

int main()
{

    float (*func)(float, int) = &fn;

    float val = func(2.5, 6);

    cout << val << '\n';

    return 0;
}

The output is 2.5.

C++ Reference

Referencing in C++ is just a way to produce a synonym (another name) for an identifier. It uses the & operator, but not in the same way as & is used for pointers. Consider the following code segment:

    int myInt = 8;
    int &yourInt = myInt;

    cout << myInt << '\n';
    cout << yourInt << '\n';

The output is:

8
8

The first statement initializes the identifier, myInt; i.e. myInt is declared and made to hold the value, 8. The second statement makes a new identifier, yourInt a synonym to myInt. To achieve this, the & operator is placed between the data type and the new identifier in the declaration. The cout statements show that the two identifiers are synonyms. To return the value in this case, you do not need to precede it with *. Just use the identifier.

myInt and yourInt here, are not two different objects. They are two different identifiers referencing (identifying) the same location in memory having the value, 8. If the value of myInt is changed, the value of yourInt will also change automatically. If the value of yourInt is changed, the value of myInt will also change automatically.

References are of the same type.

Reference to a Function

Just as you can have a reference to a scalar, you can also have a reference to a function. However, coding a reference to a function is different from coding a reference to a scalar. The following program illustrates this:

#include <iostream>
using namespace std;

float fn(float fl, int in)
    {
        return fl;
    }

int main()
{

    float (&func)(float, int) = fn;

    float val = func(2.5, 6);

    cout << val << '\n';

    return 0;
}

The output is 2.5.

Note the first statement in the main function, which makes func a synonym of fn. Both reference the same function. Note the single-use and position of &. So & is the reference operator here and not the address-of operator. To call the function, just use either name.

A reference identifier is not the same as a pointer identifier.

Function returning a Pointer

In the following program, the function returns a pointer, which is actually the address of the pointed object:

#include <iostream>
using namespace std;

float *fn(float fl, int in)
    {
        float *fll = &fl;
        return fll;
    }

int main()
{

    float *val = fn(2.5, 6);

    cout << *val << '\n';

    return 0;
}

The output is 2.5

The first statement in the function, fn() is there just to create a pointer object. Note the single-use and position of * in the function signature. Also note how the pointer (address), was received in the main() function by another pointer object.

Function returning a Reference

In the following program, the function returns a reference:

#include <iostream>
using namespace std;

float &fn(float fl, int in)
    {
        float &frr = fl;
        return frr;
    }

int main()
{

    float &val = fn(2.5, 6);

    cout << val << '\n';

    return 0;
}

The output is 2.5.

The first statement in the function, fn() is there just to create a reference. Note the single-use and position of & in the function signature. Also note how the reference, was received in the main() function by another reference.

Passing a Pointer to a Function

In the following program, a pointer, which is actually the address of a float pointed object, is sent as an argument to the function:

#include <iostream>
using namespace std;

float fn(float *fl, int in)
    {
        return *fl;
    }

int main()
{
    float v = 2.5;

    float val = fn(&v, 6);

    cout << val << '\n';

    return 0;
}

The output is 2.5

Note the use and position of * for the float parameter in the function signature. As soon as the evaluation of the fn() function starts, the following statement is made:

float *fl = &v;

Both fl and &v are pointing to the same pointed object that holds 2.5. *fl at the return statement is not a declaration; it means, the value of the pointed object pointed to by the pointer object.

Passing a Reference to a Function

In the following program, a reference is sent as an argument to the function:

#include <iostream>
using namespace std;

float fn(float &fl, int in)
    {
        return fl;
    }

int main()
{
    float v = 2.5;

    float val = fn(v, 6);

    cout << val << '\n';

    return 0;
}

The output is 2.5

Note the use and position of & for the float parameter in the function signature. As soon as the evaluation of the fn() function starts, the following statement is made:

float &fl = v;

Passing an Array to a Function

The following program shows how to pass an array to a function:

#include <iostream>
using namespace std;

int fn(int arra[])
    {
        return arra[2];
    }

int main()
{

    int arr[] = {000, 100, 200, 300, 400};

    int val = fn(arr);

    cout << val << '\n';

    return 0;
}

The output is 200.

In this program, it is the array that is passed. Note that the parameter of the function signature has an empty array declaration. The argument in the function call is only the name of a created array.

Can a C++ Function return an Array?

A function in C++ can return the value of an array, but cannot return the array. The compiling of the following program results in an error message:

#include <iostream>
using namespace std;

int fn(int arra[])
    {
        return arra;
    }

int main()
{

    int arr[] = {000, 100, 200, 300, 400};

    int val = fn(arr);

    return 0;
}

Pointer of a Pointer

A pointer can point to another pointer. That is, a pointer object can have the address of another pointer object. They still must all be of the same type. The following code segment illustrates this:

int ptdInt = 5;

    int *ptrInt = &ptdInt;

    int **ptrptrInt = &ptrInt;

    cout << **ptrptrInt << '\n';

The output is 5.

In the declaration of pointer-to-pointer, double * is used. To return the value of the final pointed object, double * is still used.

Array of Pointers

The following program shows how to code an array of pointers:

include <iostream>
using namespace std;

int main()
{
    int num0=000, num1=100, num2=200, num3=300, num4=400;
    int *no0=&num0, *no1=&num1, *no2=&num2, *no3=&num3, *no4=&num4;

    int *arr[] = {no0, no1, no2, no3, no4};

    cout << *arr[4] << '\n';

    return 0;
}

The output is:

400

Note the use and position of * in the declaration of the array. Note the use of * when returning a value in the array. With pointers of pointers, two * are involved. In the case of an array of pointers, one * has already been taken care of, because the array identifier is a pointer.

The array of Variable Length Strings

A string literal is a constant that returns a pointer. An array of variable-length strings is an array of pointers. Each value in the array is a pointer. Pointers are addresses to memory locations and are of the same size. The strings of the different lengths are elsewhere in memory, not in the array. The following program illustrates the use:

#include <iostream>
using namespace std;

int main()
{

    const char *arr[] = {"woman", "boy", "girl", "adult"};

    cout << arr[2] << '\n';

    return 0;
}

The output is “girl”.

The declaration of the array begins with the reserved word, “const” for constant; followed by “char” for a character, then the asterisk, * to indicate that each element is a pointer. To return a string from the array, * is not used, because of the implicit nature of the pointer of each string. If * is used, then the first element of the string will be returned.

Pointer to a Function returning a Pointer

The following program illustrates how a pointer to a function returning a pointer is coded:

#include <iostream>
using namespace std;

int *fn()
    {
        int num = 4;
        int *inter = &num;
        return inter;
    }

int main()
{

    int *(*func)() = &fn;
    int val = *func();

    cout << val << '\n';

    return 0;
}

The output is 4.

The declaration of a pointer to a function returning a pointer is similar to the declaration of a pointer to an ordinary function but preceded with an asterisk. The first statement in the main() function illustrates this. To call the function using the pointer, precede it with *.

The Arrow Operator, ->

Class and Objects

A class is a set of variables and functions that work together; where the variables do not have values assigned to. When values are assigned to the variables, the class becomes an object. Different values given to the same class result in different objects; that is, different objects are the same class with different values. Creating an object from a class is said to be instantiating the object.

The programmer decides on the name to give to the class and the names to give to the objects.

A function that belongs to a class is needed to instantiate an object from the class. In C++, that function has the same name as the name of the class. Objects created (instantiated) from the class have different names given to them, by the programmer.

Creating an object from a class means constructing the object; it also means instantiating.

If such an object is a pointer, then to access a member of the object, the -> operator is used instead of the dot. The following code illustrates this:

#include <iostream>
using namespace std;

class TheCla
    {
        public:
        int num;
    };

int main()
{
    TheCla myObj;
    TheCla *myObjPtr = &myObj;
    myObjPtr->num = 6;
    cout << myObjPtr->num << '\n';

    return 0;
}

The output is 6.

The same scheme applies to structs and unions.

Conclusion

To create a pointer to a scalar, do something like,

float pointed;

float *pointer = &pointed;

* has two meanings: in a declaration, it is indicating a pointer; to return something, it is for the value of the pointed object.

The array name is a constant pointer to the first element of the array.

To create a pointer to a function, you can do,

int (*func)() = &fn;

where fn() is a function defined elsewhere and func is the pointer.

& has two meanings: in a declaration, it indicates a reference (synonym) to the same object as another identifier; when returning something, it means the address-of.

To create a reference to a function, you can do,

float (&refFunc)(float, int) = fn;

where fn() is a function defined elsewhere and refFunc is the reference.

When a function returns a pointer, the returned value has to be received by a pointer. When a function returns a reference, the returned value has to be received by a reference.

When passing a pointer to a function, the parameter is a declaration, while the argument is the address of a pointed object. When passing a reference to a function, the parameter is a declaration, while the argument is the reference.

When passing an array to a function, the parameter is a declaration while the argument is the array name without []. The C++ function does not return an array.

A pointer-to-pointer needs two * instead of one, where appropriate.

Chrys

About the author

Chrysanthus Forcha

Chrysanthus Forcha

Discoverer of mathematics Integration from First Principles and related series. Master’s Degree in Technical Education, specializing in Electronics and Computer Software. BSc Electronics. I also have knowledge and experience at the Master’s level in Computing and Telecommunications. Out of 20,000 writers, I was the 37th best writer at devarticles.com. I have been working in these fields for more than 10 years.