Memory and pointer
We learned how to declare variables array, vector etc. But how do these things works under the hood? Lets start with memory.
All the are stored in memory. For example if we declare array or vector, c++ will allocate new memory, which we can then use directly.
If we no longer need these variable, they will be cleaned up automatically, like variables declared within a function. But, how do we know where they are stored? In this case, we use pointers. A pointer is a variable that can store a memory location. We can find the memory location of a variable using the &
operator.
int n = 19;
int *pointer_n = &n; // pointer of the n
In this example, the value of pointer_n
is the memory location of the variable n
. If we change the value pointed to by pointer_n
using *pointer_n = 10, it will change the value stored in the memory location of the variable n
. And if we change the value of n
and then print the value pointed to by pointer_n
, it will print the updated value of n
. Because it’s connected to n
.
![Image](./images/memory.drawio.png)
If we look at the image, pointer_n
connected to variable n
.
int a[4];
a[0] = 12;
int *p = a;
In this example, the value of p
is the memory location of the array a. Specifically, it is the starting location of this array.
![Image](./images/memory2.drawio.png)
now we have general idea of a pointer.
Dynamic Memory Management
In C++, we can allocate new memory whenever we want using the new operator. It will allocate new memory and return a pointer to the location. Let’s see an example:
int *n = new int;
*n = 13;
cout << n << " " << *n << endl;
delete n;
In this case, we are allocating memory for a new int variable. After using it, we delete it to free the memory. When we allocate variables dynamically, we must track and manage them ourselves. Let’s see another example:
int *n = new int[10];
*n[0] = 1;
delete[] n;
In this case, we are allocating memory for an array that can store 10 int variables and getting the first memory location of this array. To access the 5th element, we can use n[4], as we know the size of an int.
Why do we need
We can allocate new memory dynamically and manage it ourselves. But why do we need this? Some data structures require such manual memory management. However, we must track and free our memory after we finish using it. In complex systems, it’s hard to track memory usage and ensure safety. That’s why languages like Java, Go, and Python use garbage collectors. Some newer languages, like Rust, use different types of memory management, such as the borrow checker. I will write about this in the future.