Pointers and Their Usage
Learn about pointers and their use in programming.
We'll cover the following
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;// ORpointer_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.
#include <iostream>using namespace std;int main(){int x = 10;int *ptr = &x;cout << "Value of `x` is: \t\t\t\t"<< x<<'\n';cout << "Address of variable `x` is: \t\t\t" << &x <<'\n';cout << "Value of pointer `ptr` is: \t\t\t" <<ptr <<'\n';cout << "Address of pointer `ptr` is: \t\t\t" << &ptr <<'\n';cout << "Value of the memory referred by `ptr` is: \t" << *ptr <<'\n';return 0;}
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 hereint 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;}
The
intSwap_withPointers.cpp
file: In this example, we have written avoid SWAP(int * pa, int * pb)
function that takes two integer pointers,pa
andpb
, as parameters. This function performs the swapping of the values pointed to by two pointers. It achieves this by first creating a temporary variabletemp
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, themain()
function calls thisSWAP()
function with two addresses of the integer variables, namelya
andb
, 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 ofdouble
memory location; therefore, the receiving pointers should bedouble *
. So, on line 4, we have two pointers,double * pa
anddouble * pb
, as parameters. Also, the variabletemp
should bedouble
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 theSWAP()
function for every data type, we make aSWAP()
template function. We can see in the code that instead of using a specific data type, we’ve used the generic template typeT
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 theT
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 namestd::swap()
(or just by writingswap()
if we have writtenusing 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.