Pointers in C++
C++ pointers are a fundamental feature that allows direct memory access and manipulation, enabling dynamic memory allocation, efficient array handling, and the implementation of complex data structures. Understanding pointers is crucial for leveraging C++'s full potential in creating efficient and powerful applications.
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:
Note: The reason we declare data types of pointers is to know how many bytes of data are 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.
How to use Pointers in C++?
We have to follow a few steps to use pointers in C++:
- Create a pointer variable.
- Assign the address of another variable to the pointer using the & operator.
- Access the value at the address using the \ operator.
Symbols Used in Pointers
The following table shows the symbols that are used with pointers.
|Address of operator
|Used to find the address of a variable
|Used to access the value at an address
Let us now take an example to understand pointers:
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).
Pointers and References in C++
In C++, pointers and references provide two different ways to manipulate data through their memory addresses, enhancing the flexibility and efficiency of function calls and data handling.
Pointers are variables that store memory addresses of other variables. They allow for dynamic memory allocation and manipulation, enabling direct access and modification of the value stored at the pointed-to address. When passing arguments to a function using pointers (call-by-reference with a pointer argument), changes made to the pointer-dereferenced values within the function reflect in the original arguments.
Example of Passing by Pointer:
References in C++ are an alternative to pointers, providing an alias for another variable. Unlike pointers, references cannot be null and do not require dereferencing to access the value they refer to, making them safer and easier to use in some cases. Passing arguments by reference (call-by-reference with a reference argument) allows the function to operate directly on the original variables, similar to pointers, but with a more straightforward syntax.
Example of Passing by Reference:
Comparison and Usage
Call-By-Value: In this method, a copy of the variable is passed to the function, meaning any modifications within the function do not affect the original variable. This is suitable for small data types where the overhead of copying is minimal.
Call-By-Reference with Pointer Argument: This approach passes the memory address of the variable, allowing the function to modify the original variable directly. It is useful for modifying large data structures or for dynamic memory management.
Call-By-Reference with Reference Argument: This method provides a more intuitive and safer way to modify the original variable without the syntactic complexity of pointers. It is often preferred for passing objects of user-defined types.
Array Name as a Pointer in C++
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.
In the above example, we assigned the address of arr 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 the next section).
Pointer Arithmetic and Pointer Expressions in C++
We can only perform a limited number of arithmetic operations on pointers in C++. These arithmetic operations are:
- Increment Operator (++)
- Decrement Operator (--)
- Addition (+)
- 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 the 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.
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 (because the size of an int is ) when we used ptr++.
Example 2: Using Decrement Operator
The decrement operator (--) is similar to the increment operator. The 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.
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.
In the above example, ptr1 + 2 is equivalent to &arr, and ptr2 - 1 is equivalent to &arr.
Pointers and String Literals in C++
String literals are the arrays that contain null-terminated character sequences (\0). Each element of a string literal is of the type const char.
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:
As we know, arrays and pointers in C++ have the same behaviour in expressions; we can use ch_ptr to access the characters of the string literal.
In the above example, both ch1 and ch2 point to the same character of the string literal.
Pointer to Pointer
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.
To declare a pointer to a pointer, we use one unary operator (*) for each level of chaining of pointers.
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, also known as void*, reference variables without a specific data type. They are versatile and can be typecasted to store any data type's address. However, they cannot be directly dereferenced. To access their content, you must convert them to a pointer of a specific data type.
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.
A C++ pointer is valid if it is either:
- Pointing to an object, or
- Addressing an out-of-bounds element in an array, beyond the range of array_name to array_name + array_size (inclusive).
On the contrary, an invalid pointer fails to meet these criteria. A valid pointer may become invalid when the object it references is deallocated, ending its lifecycle. Invalid pointers can lead to unexpected program behavior and should be minimized.
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 + 7 does not point to any object in this program.
In C++, we can assign NULL to a pointer. NULL has a value of zero, and when a pointer is assigned this value, it's referred to as a null pointer. Using NULL, we can create valid pointers without storing a variable's address in the pointer. It's advisable to assign NULL during pointer declaration to prevent potential runtime errors.
As we can observe, we created a null pointer and then printed its value in the above example.
Advantages of Using Pointers
- Pointers enable dynamic memory allocation and deallocation.
- They efficiently manage arrays and data tables.
- Pointers facilitate multiple value returns from functions by modifying argument values through their addresses.
- They excel in handling dynamic data structures such as linked lists and trees.
- Pointer in C++ stores memory addresses of variables, other pointers, and functions.
- Function arguments can be passed in three ways: call by value, call by reference with a pointer argument, and call by reference with a reference argument.
- Arrays support four arithmetic operations: increment, decrement, addition, and subtraction.
- The const keyword can be used with pointers, enabling iteration through array elements.
- Pointers may become invalid when not NULL, not pointing to valid memory, or exceeding array bounds.
- Void pointers have no specific data type, and pointers can be assigned the value NULL.