Pointers and Their Usage

Learn about pointers and their use in programming.

Pointers

In programming, a pointer is a variable that stores the memory address of another variable. To create a pointer, we use the type keyword followed by an asterisk (*) symbol followed by the name of the pointer variable. The syntax for creating a pointer of a particular type is shown below.

type * pointer_name;

Here, type is the type of memory the pointer points to and pointer_name is the name of the pointer variable.

To initialize a pointer with the memory address of a variable, we use the address-of operator (&) followed by the name of the variable. The syntax for creating a pointer and initializing it with the address of a variable is shown below.

type *pointer_name = &variable_name;
// OR
pointer_name = &variable_name; // if pointer_name is already declared

The size of the pointer

On a 16-bit architecture, pointers typically take up 2 bytes of memory, which limits the amount of memory that can be addressed to 64 kilobytes. On a 32-bit architecture, pointers typically take up 4 bytes of memory, which allows for a much larger address space of up to 4 gigabytes. On a 64-bit architecture, pointers typically take up 8 bytes of memory, which enables even larger address spaces of up to 16 exabytes.

Understanding the size of pointers is crucial when dealing with large data in programming. Pointers play a vital role in determining how much memory the program can access and utilize. For systems with larger memory, larger pointer sizes are necessary to efficiently handle the demands of extensive datasets. It’s essential to be aware of the variations in pointer sizes across different computer architectures and the limitations they impose on the amount of memory the program can effectively use. This knowledge becomes especially critical during the design and development of applications that involve large datasets. By considering pointer size and memory limitations, developers can ensure that their programs perform optimally, making it easier to work with significant amounts of data while avoiding potential memory-related challenges.

Here’s an example in which we create a pointer of int type, pointing to an integer memory, followed by an illustration showing how the memory can be visualized.

Press + to interact
#include <iostream>
using namespace std;
int main()
{
int x = 10;
int *ptr = &x;
cout << "Value of `x` is: "<< x<<'\n';
cout << "Address of variable `x` is: " << &x <<'\n';
cout << "Value of pointer `ptr` is: " <<ptr <<'\n';
cout << "Address of pointer `ptr` is: " << &ptr <<'\n';
cout << "Value of the memory referred by the pointer `ptr` is: " << *ptr <<'\n';
return 0;
}
  • Line 6: We declare an integer variable x and initialize it with a value of 10.

  • Line 7: We declare an integer pointer ptr and initialize it with the memory address of the variable x using the address-of operator &.

  • Line 8: We display the value of the variable x.

  • Line 9: We display the address of a variable x using the reference variable preceded by the address-of operator &.

  • Line 10: We display the value stored in the pointer ptr, which is actually the address of the variable x.

  • Line 11: We display the address of the pointer ptr using the address-of operator &.

  • Line 12: We display the content of memory that is referred to by the pointer ptr using the asterisk * symbol. This is called dereferencing a pointer.

Note: Dereferencing a pointer involves accessing the value stored in the memory location that the pointer is pointing to. This is typically done using the * operator followed by the pointer name, such as *pointer_name. When we dereference a pointer, we are essentially accessing the data stored in the memory location pointed to by the pointer, allowing us to manipulate or retrieve that data in our code.

Visually, we can see the physical and logical representation of the pointer pointing to a memory in the following two images:

Press + to interact
canvasAnimation-image
1 of 2

Pointers are the most important abstraction that C++ holds in working with several programming constructs. They allow us to manipulate data in ways that would be impossible with other languages. Pointers enable us to create dynamic data structures, pass data between functions, and even access hardware directly.

Example: Swapping using pointers

Let’s dive into a straightforward example: say we want to write a function that can take the addresses of two writable memory locations and swap the values stored in those locations. While it may seem like a simple task, this actually requires some clever manipulation of pointers. By understanding how to work with memory addresses and pointers, we can create powerful functions that can transform our data in all sorts of useful ways. So, let’s roll up our sleeves and explore the power of pointers with this simple yet important function.

#include <iostream>
using namespace std;
void SWAP(int * pa, int * pb)
{
int temp = *pa; // temp is assigned with the first value (pointed by pa)
*pa = *pb; // the first memory is being replaced by the second one (pointed by pb)
*pb = temp;// the second memory is replaced by temp (which is stored on line 6)
}
int main()
{
// Our code goes here
int a=10, b = 20;
cout << "Values before swapping: a = "<< a << " b = "<<b << endl;
SWAP(&a, &b);
cout << "Values After swapping : a = "<< a << " b = "<<b << endl;
return 0;
}
Swapping the integers using pointers
  • The intSwap_withPointers.cpp file: In this example, we have written a void SWAP(int * pa, int * pb) function that takes two integer pointers, pa and pb, as parameters. This function performs the swapping of the values pointed to by two pointers. It achieves this by first creating a temporary variable temp to store the value pointed to by the first pointer, pa, and then assigning the value pointed to by the second pointer, pb, to the memory location pointed to by the first pointer, pa. Finally, it assigns the value stored in the temporary variable (that is, the value at the first memory location) to the memory location pointed to by the second pointer, pb, completing the swap operation. On line 16, the main() function calls this SWAP() function with two addresses of the integer variables, namely a and b, and prints their values before and after the swapping.

Tip: Change the value inside the main() function (a = 30, b = 40) and see how it works.

  • The doubleSwap_withPointers.cpp file: It’s the exact same code except for a difference in parameter receiving. On line 16, the two arguments are the addresses of double memory location; therefore, the receiving pointers should be double *. So, on line 4, we have two pointers, double * pa and double * pb, as parameters. Also, the variable temp should be double now. The rest of the dereferencing on lines 6–8 will be exactly the same.

Note: Whenever we dereference a pointer using the * operator and a pointer name, such as *pointer_name, we can expect to retrieve the data in bytes that is equivalent to the size of the data type of the memory being pointed to. Essentially, dereferencing a pointer allows us to access and manipulate the data stored in memory, providing us with greater control and flexibility in our code.

  • The genericSwap_withPointers.cpp file: Now, instead of creating the SWAP() function for every data type, we make a SWAP() template function. We can see in the code that instead of using a specific data type, we’ve used the generic template type T defined on line 4.

Note: It's important to understand that while it may look like that we've created a single template function, SWAP(), that can automatically determine whether it should swap 4 bytes for integers or 8 bytes for doubles, the compiler actually generates separate copies of the function for each specific data type used in the code. This means that the T in the function declaration must be replaced with the actual data type being used, and the function must be called with the appropriate data type argument. In other words, the compiler creates distinct function instances for each data type used in the code, allowing for correct and efficient data swapping.

  • The generic SWAP() function is included in the standard library and can be accessed using the name std::swap() (or just by writing swap() if we have written using namespace std in the including header files region). It can be utilized whenever needed without requiring its details to be specified. We’ll be using this function in the coming lessons, so it is important to be familiar with its name and functionality. For a better understanding, you can check out this resource for the std::swap function.

Remember: In C language, pointers play a pivotal role in managing memory and enabling efficient data manipulation. Unlike some other programming languages like C++, C doesn't have reference variables. Consequently, when data needs to be passed to a function for manipulation, it can only be done by passing the memory address (pointer) of that data. Once inside the function, dereferencing the pointer allows access to the actual data stored in that memory location. This powerful mechanism provides C programmers with a direct way to work with memory, allowing for flexible and efficient data handling.