Pointers in Programming — What, Why, and How
** A small correction to the code snippets. When it comes to printing pointer variables all “%d” should be replaced by “%p” s(line 17, 19 & 20 in 1st snip, line 27 in 2nd snip, etc) because ‘d’ reinterpret the value as a 32 bit signed decimal that might print negative values as well and it won’t work /prints wrong address, with 64-bit pointers.**
Pointers in programming
Many students who start to learn programming find it somewhat difficult to grab the idea behind pointers and the importance of it. To be honest, pointers can be a bit intimidating at first. But if you know about pointers and how to use them you will have more control over what you do. So first I will discuss the basics of pointers and somewhat advanced array pointers. And then I will discuss two important uses of pointers, function declarations using the pass-by-reference method and dynamic memory allocation. I urge you to read this article more slowly if you are a newbie. Otherwise, you may feel it is overwhelming. With that being said, let’s head right into our discussion.
What is a pointer
In simple terms, a pointer is an object who keeps a memory address of a variable and that can point to the value stored in that variable. Now I know, if you are a newbie you might find it difficult to understand this idea. The idea is so simple. lemme explain it through a piece of code.
We declare a pointer using the * sign, and we obtain the starting address (base address) of a variable using the & sign. So in line 8 I have defined a pointer p using * sign and assigned the address of variable a in line 9. so in printf section first I simply print the value of a. And then print the memory address of stored variable a. so 6422036 is the starting memory location of variable a. As you know an integer is 4byte long, it takes memory from 6422036 to 6422039. Memory address points to the first memory location. Then I print the value of p. So the pointer stores the memory location of variable a. Then using the * sign in front of p, it points to the value stored in that memory location. basically, this is all you need to know about pointers. read it again if you didn’t grab the idea. Just for completeness, I defined a pointer to pointer q(**) in line 11 and assigned the memory address of pointer p to it. Now just like I used pointer p to access variable a, now I can point to pointer p. Then as p is also a pointer, I can go one step further and point to the end variable a. One thing that you should keep in mind is that C is strongly typed language. So when you pointing to an integer it is strongly advised to use an integer pointer. Because size is varied depending on the data type. This may lead to errors that may become hard to debug.
Pointers in arrays
This section is more advanced compared to the rest. Hold tight :). So array is a structure with contiguous space and the way C/C++ languages works is that when you initialize an array, the array name will be a pointer. For the 1D int array, the array name will point to the first integer value. For example, take array name as A, then A is equivalent to &A[0]. Then either using square brackets or using pointer arithmetic you can traverse the array. So let’s see a code snippet.
So in this code, I have first printed the value of array name A and it gave the starting address of the array. Then as this a 1D int array I can always specify the value that I want to take using square brackets, A[i]. And then I print the addresses of these values, &A[i]. Note that memory addresses are incremented by 4 and not by 1. Because as you know an integer uses 4 bytes. Then as A points to the integer values using A+i, I can get the ith address. Finally using the * sign in front of the pointer, I can obtain the value of the array element. A[i] is same as *(A+i). Then I have simply shown how to pass arrays as a function parameter. I will describe this at the end of the next section.
Now let’s see how pointers work in a multidimensional array. Sometimes you may not grab this at the first glance. Even if you want to skip this section that’s okay because we can manipulate arrays even without pointers. I just wanted to add this for the sake of completeness. So to keep things simple I will consider a 2D int array of name A.
Array A is a stack of 3, 1D arrays. So as earlier A works as a pointer. In the 1D case, the array pointed to the first integer. But this time unlike the previous case, A points to the first1D array which is equal to &A[0]. Moving one step further *A points to the first integer in the first 1D array. Then *(*A) gives the first integer value (A[0][0]). Line 35, 36 points to the second 1D array, and the address returned is 8 bytes away from the base address as expected. And as you can see there are two ways to call that. A[i] = *(A+i) and returns a pointer to an integer in ith 1D array. The below equation will return the value of an integer and this is held for the entire array. As an example, I have shown an instance in the code above.
A[i][j] = *(A[i] + j) = *(*(A + i) + j)
Line 38 is self-explanatory. In line 39 first I get the pointer to the first integer in the 3rd 1D array. This returns that address. Then I increment the address to go to the second integer. Then using the * sign I get the value pointed by that address which is 4. Then in line 40 using A+2, I go to the 3rd 1D array. Then using *, I point to the first integer in the 3rd 1D array. The rest is same as line 39. So that’s pointers in arrays. Though this section is slightly complex, if you understood how pointers work, you can easily scale this idea up to the 3rd and 4th-dimensional arrays easily.
Use cases of pointers
- function definition — call by reference
before saying anything let’s first see a code snippet.
A simple code. I initialized a variable a with value of 10. Then using a function I incremented it. And then I print the value after incrementing. Did you notice something? Even after incrementing, the value is still 10! Inside the increment function, it has incremented. But when I come back to the main code it still has the old value! why? and the memory addresses are different! why?
So this is the way many languages work. On your left is a typical memory architecture. Code is where the instructions get stored. Static memory is where global variables are held. Heap is used for dynamic memory allocation (more on this later). Stack is where local variables are held. A local variable is a variable that is bounded by a scope. A scope is a code space inside {}. it can be a function definition, a for loop, a while loop, etc. So once you move out of scope, those variables which were defined inside the scope (local variables) get cleared. This is one thing you should keep in mind. Next is once you called a function only the value is copied to local parameters and not the actual variable (if it is an array then this isn’t an issue as arrays are always passed by reference, more on this later). This is why we had different memory addresses for variable a in main and increment functions. So when you first initialize a variable a in the main function and then when you called increment function a new int a got created in the stack under the increment function scope and it gets incremented. Then when you moved back to the main code, the local variable in increment is cleared. So variable a in main still has the value 10. So I hope now you understood the reason behind the above case. There are workarounds for such an instance and pointers are the biggest candidate. Let’s see how to use pointers to solve this issue.
Take a look, the issue is resolved right! This is what we call a function call by reference. What I do here is instead of sending the value of a I send the memory address of a to the increment function. The increment function takes it as a pointer. So inside the increment function, we can use a pointer to access the value stored in that address with the * sign. Using a pointer to manipulate the pointed variable is called Dereferencing.
Now let’s go back to the array pointer section’s code block. In that, I have passed an array by reference. So let’s go through it. Arrays are always passed by the reference. It doesn’t copy the entire variable into local space. Because this will lead to unnecessary memory leaks. So we instead just create a pointer to that array and pass it. So you can use this pointer to traverse the array inside the function. So when you pass an array as a function argument (the array pointer), there is no way you can get the size of the array from the pointer variable. So either you need to know or you have to pass the size. That’s why I have passed it as a parameter. I hope now you can fully understand that code snippet.
02. Dynamic memory allocation
Let’s see a code snippet first.
This will give an error. Because at the time of compilation, the compiler doesn’t know the size of the memory needs to allocate in the stack to array A. So in these instances, you need to allocate the memory space dynamically while the code runs. This is called dynamic memory allocation and is where heap memory comes in. Heap is a free memory space accessible by any context for this use case. While stack is an implementation of stack data structure, a heap is not an implementation of heap data structure. Heap is only accessible via referencing because what commands do is, looks for a free space, reserve it, and give back the pointer to that location. So you have to use that pointer to manipulate the data inside the heap. But one thing you always have to keep in mind is that you have to free the space after you are done working with it. Otherwise, it will keep the space reserved for long as code runs. Let’s see a basic example of how to allocate memory at run time.
line 15, malloc is the function used in C for dynamic memory allocation. What it does is search for a contiguous space of required size, reserve it and provide the base address of the reserved slot as a void pointer (there is another command called calloc which does the similar thing but initializes the array with value 0 for each array location). And then using (int *) I have to typecast (change the data type from void to int) and feed-in into an integer pointer A. So now I have a contiguous space of size n to use for my int array. Then I fill the array and print each character with space in between. After I am done working with it, I freed the pointer. Purposely I used two ways to access array elements (pointers, square brackets). In C++ you can use new and delete operators for the above case. So this is how you dynamically allocate memory.
So that’s it for this article. Hope you learned something new. Knowing pointers really makes you stand above the rest as many learners skip learning pointers because it is kinda intimidating at the beginning. I wish you Good luck and stay tuned for upcoming articles.