Pointers in C++

Challenge Inside! : Find out where you stand! Try quiz, solve problems & win rewards!

Video Tutorial

Pointer Variables

Overview

When we declare a variable in C++, a specific location in memory is assigned to it to store a value in this variable. This location is called the memory address of the variable. Pointers in C++ are special variables that store the memory address of other variables. Pointers add more features and flexibility to the C++ programming language.

Scope

  • This article covers the meaning of pointers, different types of pointers in C++, and how to use them.
  • This article also covers pointer arithmetic, pointers with arrays, and constants with pointers.

What are Pointers?

A pointer in C++ is a variable that stores the address (or memory location) of another variable. In other words, a pointer points to the address of another variable. Like regular variables, pointers in C++ have data types. A pointer should have the same data type as that of the variable it points to.

Note: The reason we declare data types of pointers is to know how many bytes of data is used by the variable it stores the address of. If we increment (or decrement) a pointer, we increase (or decrease) the pointer by the size of the data type it points to.

Reference Operator and Dereference Operator

Pointers and reference, dereference operators go hand in hand. Let us learn about each of these operators.

Reference Operator (&)

The reference operator (&) returns the address of any variable (including pointers). For example:

Output:

Note: that the address of each variable is different in different systems.

As we know, pointers in C++ are used to store addresses of variables. In order to store the address of any variable in a pointer, we use the reference operator. In other words, we can assign addresses to pointers using the reference operator.
Example:

Output:

The pointer variable ptr now contains the address of the variable var.

Dereference Operator (*)

The Asterisk symbol (*) is called dereference operator when it is used with pointers. We can access the values stored in a variable to which pointer points to, by using the pointer's identifier and the dereference operator.

In relation to pointers, the asterisk symbol (*) has two different meanings. When * is used in a variable declaration, the value written on the right-hand side of the = sign should be an address of a variable (present in the memory). The unary operator (*) when used with a pointer allows us to retrieve or assign a value stored in the memory location pointed by the pointer. The unary operator can be read as "value pointed by".

For example:

Output:

As we can observe, to get the value of the variable var, we used *ptr.

Pointer Declaration Syntax

Pointers in C++ are declared using the following syntax:

We use the asterisk (*) symbol to designate a variable as a pointer in C++. The asterisk symbol can be placed anywhere before the pointer name and after the datatype.

If we have to declare two (or more) pointers together in the same line, we will need to use the asterisk symbol before each variable name. For example:

How to use Pointers in C++?

We have to follow a few steps to use pointers in C++:

  1. Create a pointer variable.
  2. Assign the address of another variable to the pointer using the & operator.
  3. Access the value at the address using the \ operator.

Symbols Used in Pointers

The following table shows the symbols that are used with pointers.

SymbolNameDescription
&Address of operatorUsed to find the address of a variable
Indirection operatorUsed to access the value at an address

Let us now take an example to understand pointers:

Output:

In the above example, we used the reference operator to store the address of var in the pointer ptr. Then, we changed the value of the variable var by using the dereference operator with the pointer (*ptr).

Ways to Pass C++ Arguments to a Function

In C++, we can pass arguments to functions in three different ways. These are:

  • Call By Value
  • Call By Reference with Pointer Argument
  • Call By Reference with Reference Argument

Let us take a brief overview of each one of them.

Call By Value

By default, C++ uses the call by value method. This method copies the real value of an argument into the function's parameter. So, if the parameter inside the function is changed, it will not affect the argument.

Let us take an example to understand the call by value method.

Output:

From the above example, we can observe that the address of variable var was different inside the function triple(). Also, altering var inside the function triple() did not have any impact on var present in the main() function.

Call By Reference With Pointer Argument

In call by reference with pointer argument, we pass the variables' address as the arguments to the parameters of a function. That's why the original variables are modified if we make changes to the function's parameters.

Let us take an example to understand the call by reference with pointer argument.

Output:

Because we used call by reference in the above example, the address of variable var was the same in both triple() and main() functions. This means both var variables share the same memory location. That's why changing the value of var inside triple(), resulted in the change in var inside main().

Call By Reference With Reference Argument

In call by reference with reference argument, we pass the variables' address as the arguments.There is only one difference between the two types of call by references. Call by reference with pointer argument takes pointers (that point towards the memory location of arguments) as the function's parameters. On the other hand, call by reference with reference argument takes the original variable itself (not a copy of variables) as the function's parameters.

To pass the original variable as a function's parameter, we use the reference operator (&) in the declaration of a function's parameters.

Let us Take an Example to Understand the Call by Reference With Reference Argument.

Output:

In the above example, as we have defined int& as the parameter instead of int, the original variable var was passed as an argument in the function triple().

Advanced Pointer Notation

We can access the elements of a 2D array using pointer notation. The elements of a 2D array are stored in a row-wise manner. We can say that a 2D array is a collection of multiple 1D arrays placed one after the other.

For example, consider the following array:

2d array using pointer notion

In the image above, we can consider each row as a 1D array. So, the array arr contains 22 elements where each element is a 1D array containing 33 integers.

We know that arr points to the 0th0^{th} element i.e. the 0th0^{th} 1D array. Similarly, arr + 1 points to the 1st1^{st} element i.e. the 1st1^{st} 1D array. The image below represents the same. Row as 1d array

In general, we can say that arr + n points to the nthn^{th} element of the array arr, where the nthn^{th} element is a 1D array. Hence, dereferencing arr + n (i.e. *(arr + n)) will give us the address of the 0th0^{th} element (base address) present in this 1D array.

Now that we know the base address of the nthn^{th} 1D array, we will be able to get the address of the mthm^{th} element of the 1D array by using *(arr + n) + m. Finally, dereferencing *(arr + n) + m, i.e. *(*(arr + n) + m), will give us the value of the individual integer of the 2D array.

So, in the above example, *(*(arr + 1) + 2) will return 22. Similarly, *(*(arr) + 1) will return 11.

In general, arr[n][m] is equal to *(*(arr + n) + m).

Now, let us see how to access each element of this array using the advanced pointer notation.

Array NotationPointer NotationArray's Element
arr[0][0]*(*arr)10
arr[0][1]*(*arr + 1)11
arr[0][2]*(*arr + 2)12
arr[1][0]*(*(arr + 1))20
arr[1][1]*(*(arr + 1) + 1)21
arr[1][2]*(*(arr + 1) + 2)22

Array Name as Pointers

The name of an array acts like a pointer because the address of the first element of an array is stored in its name. So, if a pointer contains the address of the first element of an array, we can use that pointer to access all the elements of the array.

For example:

Output:

In the above example, we assigned the address of arr[0] to the pointer ptr. That's why we are able to access all the elements of the array using the pointer (we will see this in a clear way in next section).

Pointer Expressions And Pointer Arithmetic

We can only perform a limited number of arithmetic operations on pointers in C++. These arithmetic operations are:

  1. Increment Operator (++)
  2. Decrement Operator (--)
  3. Addition (+)
  4. Subtraction (-)

Let us understand pointer arithmetic with the help of a few examples.

Example 1: Using Increment Operator

When we increment a pointer using the increment operator (++), the address of the pointer increases. The increase in address of the pointer is equal to the size of its data type.

As all the elements of the array are stored in contiguous memory, we can use the increment operator on pointers to access the elements of an array.

Output:

In the above example, we used ptr++ to access each element of the array arr. Since ptr had an int type, the address was increased by 44 (because size of an int is 44 ) when we used ptr++.

Example 2: Using Decrement Operator

The decrement operator (--) is similar to the increment operator. Decrement operator decreases the address of a pointer by the size of its data type.

The decrement operator can also be used with arrays to access their elements.

Output:

In the above example, the pointer ptr was pointing to the last element of the array arr. In order to access each element of the array using ptr, we used ptr-- inside the for loop.

Example 3: Addition and Subtraction

If we add 3 to a pointer (ptr + 3), the pointer will point to the memory address located 3 places ahead of the current address. In other words, the pointer will point to an address that is three times the size of the pointer's data type ( 3 * size_of_pointer_type).

The subtraction operation is similar to addition. In case of the subtraction operation in pointers, if we subtract 1 from the pointer (ptr - 1), the pointer will point to the previous memory address.

Output:

In the above example, ptr1 + 2 is equivalent to &arr[2], and ptr2 - 1 is equivalent to &arr[3].

Pointers and Const

C++ allows us to use the keyword const in pointer declarations. We can use the const keyword with pointers in three different ways:

  • Pointers to Constant
  • Constant Pointers
  • Constant Pointers to Constant

Pointers to Constant

If we use pointers to constant, the pointer will not be able to modify the data which is stored in a memory location to which the pointer points. However, the pointer can point to the memory location of some other variable.

Syntax:

Let us take an example:

Output:

In the above example, we declared a "pointer to constant" ptr. Because of this, we could store addresses of different variables in ptr, but if we try to change the value to which ptr points to using dereference (*ptr), then the compiler would generate an error.

2. Constant Pointers

In constant pointers, the pointers point to a fixed memory location. In other words, if a constant pointer stores the address of one variable, we can not use that pointer to store the address of another variable. However, we can change the value of the variable it points to.

Syntax:

For example:

Output:

In the above example, we declared a "constant pointer" ptr. Because of this, we could change the value of the variable ptr points to, but if we try to store a different address in ptr, the compiler will generate an error.

3. Constant Pointers to Constant

In constant pointers to constant, the pointer points to a constant variable and the pointer itself is constant (i.e. it can not point somewhere else). So, we can not change the value of the variable it points to and we can not even use the pointer to store the address of a new variable.

Syntax:

For example:

Output:

In the above example, we declared a "constant pointer to constant" ptr. Because of this, we could not change the value of the variable to which ptr points, and we could not even store a different address in ptr.

Pointers to Functions

Like variables, functions also have addresses. So, C++ offers functionality to create pointers that can store these addresses. The pointers that can store the addresses of functions are called Pointers to Functions or Function Pointers.

Typically, pointers to functions are used when we need to pass a function as an argument to another function.

Syntax:

Let us look at an Example to Understand Pointers to Functions:

Output:

In the above example, we created a pointer to function fp. We used this pointer to function to store the address of the function square(). Then, we used the pointer fp itself to call the function and calculate the square of the variable num.

Pointers and String Literals

String literals are the arrays that contain null-terminated character sequences (\0). Each element of a string literal is of the type const char.

For example:

The string "hey" is an array. The pointer ch_ptr points to the first element of this array i.e. 'h'.

If we assume that "hey" is stored at the memory locations starting at address 1000, then we can represent the declarations as: pointer and string literal

As we know, arrays and pointers in C++ have the same behavior in expressions, we can use ch_ptr to access the characters of the string literal.

For example:

In the above example, both ch1 and ch2 point to the same character of the string literal.

Pointers to Pointers

A pointer to a pointer is a chain of pointers. When we define a pointer to a pointer, the first pointer points to the second pointer, and the second pointer points to the actual variable. pointers to pointers

To declare a pointer to a pointer, we use one unary operator (*) for each level of chaining of pointers.

For Example:

Output:

In the above example, we created a variable var and two pointers ptr1 and ptr2. The address of var was stored in ptr1, while the address of ptr1 was stored in ptr2.

Void Pointers

The pointers that point to a variable having no data type are known as void pointers. Since void pointers point to no particular data type, these pointers can be typecasted to any data type and can be used to store the address of any type.

Void pointers can not be dereferenced directly. To deference them, we need to convert them into another pointer type that points to any particular data type.

For Example:

Output:

In the above example, we created a void pointer ptr. Because ptr was void, we were able to hold the address of an int and a char variable in ptr. However, when we tried to dereference ptr, the compiler generated an error because we did not typecast ptr to one specific data type.

Invalid Pointers

A pointer in C++ is considered valid if:

  • It is a NULL pointer value, or
  • It points to an object, or
  • It points to an out of bounds element of an array that is, other than range array_name to arrry_name + array_size (both inclusive).

A pointer that satisfies no condition of three conditions written above, is known as an invalid pointer. A valid pointer may become invalid if the object to which (or after which) it points ends its life cycle i.e., the memory location to which it points to gets deallocated.

Invalid pointers may or may not raise errors in a program. Accessing these pointers can lead to unexpected behavior of a program. Hence, we should always avoid invalid pointers.

For example:

In the above example, we created two pointers ptr1 and ptr2. The pointer ptr1 is invalid because it does not point to any address. The pointer ptr2 is invalid because &arr[0] + 7 does not point to any object in this program.

NULL Pointers

We can assign NULL to a pointer in C++. The value of NULL is zero. A pointer that is assigned a NULL value is called a null pointer.

NULL allows us to create valid pointers, without storing the address of any variable in the pointer. It is recommended to assign NULL during the pointer declaration. Otherwise, the compiler may generate a run time error.

For example:

Output:

As we can observe, we created a null pointer and then printed its value in the above example.

Common Mistakes When Working With Pointers

Let us now take a look at a few common mistakes people make while using pointers.

  1. In the above example, var is a variable, not an address. So, we need to write &var to store the address of var in ptr.

  2. In the above example, *ptr denotes the value stored in the variable var, while &var denotes the address of var. If we want to store the value of var in *ptr, we need to remove & from &var.

Advantages of Using Pointers

Following are the advantages of using pointers:

  1. We can dynamically allocate and de-allocate memory using pointers.
  2. Pointers are more efficient in handling arrays and data tables.
  3. Pointers can be used to return multiple values from a function. This can be done by passing the arguments with their addresses and making changes to the argument's values using pointers.
  4. Pointers are efficient in handling dynamic data structures like linked lists, trees, etc.

Conclusion

  • Pointers can store the memory address of variables, other pointers, and functions.
  • We can pass arguments to a function in three ways: call by value, call by reference with pointer argument, and call by reference with reference argument.
  • We can perform four arithmetic operations on arrays: increment, decrement, addition, and subtraction.
  • We can use the const keyword with pointers and we can iterate through the elements of an array using pointers.
  • A pointer may become invalid if it is not a NULL pointer, not pointing to any object/memory, or pointing to an array index which is out of bounds.
  • Pointers without a data type are called void pointers. Additionally, we can assign NULL to pointers.

See Also: