Get up to 60 % extra points for free! More info
Only this week sale up to 80 % on e-learning courses of Java
discount week 60 - halloween

Lesson 1 - Introduction to pointers in C++

Welcome to the first lesson in the advanced course on C++ programming. In this section we'll learn to work with dynamically allocated memory in C++ and we'll also get to work with files. It'll probably not surprise you that knowledge of C++ basics is a prerequisite for passing through the series.

Addresses in memory

When we first mentioned variables, we said that a variable is a "place in memory" where we can store a value. We also know that variables have different data types (e.g. int) and they take up different amount of memory (e.g. int takes up 32 bits, i.e. 32 zeros and ones).

We can think of computer memory as a long (almost infinite :) ) series of zeros and ones. Some parts of the memory are occupied by other applications and some are perceived by the operating system as free space. In order to work with memory reasonably, we use addresses, just like for houses on the street. Addresses are usually written in hexadecimal, but they are still ordinary numbers. Addresses follow each other chronologically and there is 1 byte at each address (i.e. 8 bits, because addressing by small bits would be impractical).

Once we declare a variable in the C++ source code and run the application, C++ asks the operating system for as much memory as it needs for that variable. It gets a memory address from the system, where it can store the value of the variable (simply put).

Getting the address of a variable

So far, the C++ language has completely shielded us from the addresses, reserved the memory for us and we worked with the variables simply by their names. Let's now create a simple program that creates a variable of the int type and stores the value 56 in it. We retrive the address of this variable using the reference operator & (ampersand) and write it to the console.

int main()
{
    int a;
    a = 56;
    cout << "Variable with the value " << a << " is stored in the memory at the address " << &a << endl;
    cin.get();
    return 0;
}

Result:

Console application
Variable with the value 56 is stored in the memory at the address 0x23aadc

You see, on my computer, the system chose the address 0x23aadc. You'll have a different number there. The situation in the computer's memory will look like this:

Storing a variable in memory

(The int data type has 32 bits, so it occupies 4 times 8 bits and 4 addresses. We always specify the address of the beginning of the value.)

Pointers

It's nice to get the address number, but if we were to work with the memory like this, it'd be a bit impractical. For this reason, the C++ language supports pointers. A pointer is a variable whose value is an address somewhere in memory. However, C++ does not take a pointer as a mere number, but knows to use it as an address. Therefore, when we store something in the pointer or, on the contrary, print something from it, the address (pointer value) is not displayed, but the value to which the pointer points is used.

Let's go back to our program. This time, in addition to the variable a, we also define a pointer to the variable a. It will also be of the int type, but before its name will be the dereference operator * (asterisk).

int main()
{
    int a = 56;
    int *p_a = &a; // stores the address of a to p_a
    *p_a = 15; // stores the value 15 to the address p_a
    cout << "Pointer p_a has the address " << p_a << " and points at the value " << *p_a << endl;
    cout << "Value stored in a is " << a << endl;
    cin.get();
    return 0;
}

The application creates a variable of the int type and then a pointer to int. Pointers also always have their own data type, depending on the type of value they point to. The value 56 is stored in the variable a.

The address of the variable a is stored in the pointer p_a (so far without an asterisk), which we obtain using the reference operator &. Now we want to store the number 15 where the pointer p_a points. We'll use the dereference operator (*) and thus we'll not store the value in the pointer, but where the pointer points.

Then we write the value of the pointer (which is an address in memory, usually a large number, here we list it in hexadecimal) and then we write the value to which the pointer points. Whenever we work with a pointer value (not an address), we use the * operator.

Result:

Console application
Pointer p_a has the address 0x006ffec0 and points at the value 15
Value stored in a is 15

Passing by reference

So we can create a pointer to a variable. But what is it good for? After all, we were able to store in a variable before. One of the advantages of pointers is passing by reference. Let's create a function that takes 2 numbers as parameters and we want it to swap their values. We could naively write the following code:

// this code doesn't work
void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int main()
{
    int number1 = 15;
    int number2 = 8;
    swap(number1, number2);
    cout << "number1 stores the value " << number1 << " and number2 stores the value " << number2 << endl;
    cin.get();
    return 0;
}

Result:

Console application
number1 stores the value 15 and number2 stores the value 8

Why doesn't the application work? When calling the swap function in the main() function, the values of the variables number1 and number2 are taken and copied to variables a and b in the function definition. The function further changes these variables a and b, but the original variables number1 and number2 remain unchanged. This way, when the value of a variable is copied to the function parameter, is called passing by value.

Note: To swap 2 numbers, we need an auxiliary variable. If we wrote only a = b in the function swap(); b = a ;, the value of b would be in both variables, because the value of a has changed in the meantime.

We can pass any variable by reference by modifying the function to accept pointers in the parameters and by using the reference operator & when calling the function:

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    int number1 = 15;
    int number2 = 8;
    swap(&number1, &number2);
    cout << "number1 stores the value " << number1 << " and number2 stores the value " << number2 << endl;
    cin.get();
    return 0;
}

Result:

Console application
number1 stores the value 8 and number2 stores the value 15

Since we now pass the address to the function, it's able to change the original variable.

Some C++ programmers often use function parameters to return a value. However, this is not very clear and if we are not pressured by computational time and it's at least a little possible, the function should return only a single value using the return statement.

Passing arrays

Arrays and pointers have a lot in common in C++. Therefore, when we pass an array to a parameter of a function and change the array in it, the changes will be reflected in the original array. Unlike other types, an array is always passed by reference.

void fill_array(int array[], int length)
{
    int i;
    for (i = 0; i < length; i++)
    {
        array[i] = i + 1;
    }
}

int main() {
    int numbers[10];
    fill_array(numbers, 10);
    cout << numbers[5] << endl; // prints the number 6
    cin.get();
    return 0;
}

As we said before, an array is actually a continuous place in memory. But we must be able to address such a place somehow. We address it using a pointer. The array variable name itself is nothing more than a pointer. This means that we'll pass the following assignment operations without any problems.

int array[10];
int* p_array = array;

NULL

We can assign the constant NULL to all pointers of any type. It indicates that the pointer is empty and that it is not pointing at anything. On most platforms, NULL is equal to 0, so in some codes you may find the assignment 0 instead of NULL. This is generally not recommended due to cross-platform compatibility. We'll use this value extensively in the future.

Things to remember: Pointer is a variable in which a memory address is stored. We can work either with this address or with the value at this address using the * operator. The address of any variable is obtained using the & operator.

I think we have introduced pointers quite well.

In the next lesson, Dynamic memory management in C++, we will look at dynamic memory allocation, which is the true purpose of pointers.


 

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.

Download

By downloading the following file, you agree to the license terms

Downloaded 2x (1.6 kB)
Application includes source codes in language C++

 

All articles in this section
C++ Advanced Constructs
Article has been written for you by Richard Bleier
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
Activities

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!