YTread Logo
YTread Logo

Pointers and dynamic memory - stack vs heap

May 03, 2020
Memory is an important and crucial resource in our machine and it is always good to know the

memory

architecture, the way the operating system manages the

memory

and the way the memory is accessible to us as programmers. In this lesson we will discuss the concept of

dynamic

memory and see how to work with

dynamic

memory using C or C++. The memory allocated to a program or application on a typical architecture can be divided into four segments. A segment of memory is allocated to store the instructions that must be executed. Another section stores all static or global variables, variables that are not declared inside a function, and that have the entire lifespan of an application, they are accessible anywhere during the entire life cycle of the application as long as the application is running.
pointers and dynamic memory   stack vs heap
A section of memory is used to store all function call information and all local variables and we have also talked about the

stack

in our lesson on Call by Reference. Local variables are declared inside a function and live only until the moment the function is executed. The amount of memory reserved for these three segments: the text segment, the global variable segment, and the

stack

, does not grow while the application is running. We will explain why we use this fourth segment-

heap

again later. Let us first understand how these three segments of memory are used when a program is executed.
pointers and dynamic memory   stack vs heap

More Interesting Facts About,

pointers and dynamic memory stack vs heap...

I have a simple C program here. We have a square function that gives me the square of a number. We have another Sum Square function that is given two arguments x and y, and it returns the square of x plus y. And, in the main method, I'm simply calling this Sum Square function by passing it two arguments a and b. Let's now see what happens in memory when this program is executed. Let's say this rectangle in green here is reserved in memory as stack and the rectangle in orange is the memory reserved as static or global variable section.
pointers and dynamic memory   stack vs heap
When the program starts running, the main method is called first. When the main method is invoked, an amount of stack memory is allocated for main execution. And this total is a global variable, so it should be placed in the global section. The amount of memory allocated on the stack for the execution of main can also be called the stack frame for the main method. All the local variables, the arguments, and the information that this function should return, all of this information is stored within this stack frame. The size of a method's stack frame is calculated when the program is being compiled.
pointers and dynamic memory   stack vs heap
Now when main calls the Square of Sum method, let's write the shortcut SOS, for Square of Sum, then a stack frame is allocated for the call to Square Of Sum, all these local variables x, y and z will be placed in this stack frame particular stack. . Now, Sum of Squares calls Square, let's put a shortcut for square here again, so another stack frame for square and it will have its own local variables. At any time during the execution of the program, the function at the top of the stack is executing and the rest are paused, waiting for the previous function to return something and then resume execution.
I've drawn these play and pause buttons here, in case you don't understand. Okay, so this total is a global variable, it's here in this section. Global variable because it is not declared within a function. We can access it from anywhere, and then we go to this particular statement where we call Sum Square, and Sum Square calls Square and that's why it's called our call stack. This program may not be the best way to implement this logic. I have written this program this way so I can have some nested methods that call each other. Let's say now we are in this particular statement, we are executing this statement.
So at this stage the call stack will have these three methods. Now when this method is finished, we will return to this particular statement. As soon as the Square function returns, it will be cleared from the stack memory and now the Addition Square function will resume. Again, when the sum square ends, the control will reach this particular line, the total is equal to the sum square and the main line will resume again. Now, main will call printf, so once again printf will go to the top of the stack. Printf will end and control will return back to main and now main will end.
And now that main ends, the program will also end. So in the end, our global variables will also be cleared. In this program it was not necessary to keep the total of this variable as global. We should assign a variable as global only if it is needed in multiple places in multiple functions and is needed throughout the life of the program; Otherwise, it is a waste of memory to maintain a variable for the entire lifetime of the program execution. We had maintained a global variable in this program just to understand the concepts. Here, I should point out one more thing, when our program starts, our operating system allocates certain amount of reserved space, let's say the operating system allocates 1MB space as stack, but the actual allocation of the stack frame and the actual allocation Local variables occur from the stack at runtime and if our call stack grows beyond the memory reserved for the stack, such as if a method A calls B, B calls C and we keep calling and we exhaust all the space reserved for the stack. , then this is called stack overflow and in this case our program will fail.
A common case of stack overflow is when you write a bad recursion and your program recursions infinitely. So as we can see, there are some limitations of the stack. The memory reserved for the stack does not grow during runtime. The application cannot request more memory for the stack. So if it is 1 MB, then if the allocation of variables and functions on the stack exceeds 1 MB, our program will fail. Furthermore, the allocation and deallocation of memory on the stack is done according to a set rule. When a function is called it is pushed onto the stack, when it finishes it is popped off the stack or removed from the stack.
It is not possible to manipulate the scope of a variable if it is on the stack. Another limitation is that if we need to declare a large data type, like an array, as a local variable, then we need to know the size of the array only at compile time. If we have a scenario where we have to decide how big the array will be based on some parameter during runtime, then it is a problem with the stack. For all these problems, like allocating large amounts of memory or keeping variables in memory until the time we want, we have the

heap

.
Unlike the stack, the application heap is not fixed. Its size can vary over the life of the application and there is no set rule for memory allocation or deallocation. A programmer can fully control how much memory to use from the heap, up to what time to keep data in memory over the life of the applications, and the heap can grow as long as the system does not run out of memory. That is also a dangerous thing and we must be very careful when using the heap for this reason. We also sometimes call it a heap, free memory pool, or free memory store.
We can get whatever we want from the pile. How the operating system, language runtime, or compiler implements the heap can vary, which is a matter of computer architecture. But an abstract way of looking at the heap as a programmer is that it is a large pool of free memory available to us that we can use flexibly according to our needs. The heap is also called dynamic memory, and its use is called dynamic memory allocation. Let's now see how to use the heap in our C or C++ program. I'll delete this code on the left and draw one more rectangular block here for the heap.
There is one more thing I should point out before we move forward. Heap is also a data structure and if you don't already know this Heap data structure, you will learn it in your Data Structure course. But this nomenclature here has nothing to do with the heap data structure. The term heap is used only for the large pool of free memory. The heap data structure does not appear anywhere in this context. This term often confuses many people when they know the heap data structure. The stack is also a data structure, but the stack segment of memory is actually an implementation of the stack data structure, but the heap is not an implementation of the heap data structure.
To use dynamic memory in C, we need to know four functions malloc, calloc, realloc and free. To use dynamic memory in C++, we need to know two operators new and delete. These four functions can also be used in C++, since C++ has backward compatibility. It is just a superset of C. But C++ programmers mainly use these two operators, new and delete. We will look at some code examples and try to understand how things happen when using dynamic memory. Let's first take some C code examples. Let's write a C program. I'll clean up some of the things on the right. 1MB for stack, this was just a guess.
Actually the stack size will be decided by the operating system and the compiler. It's a question of computer architecture. Coming back to the code, if we declare a variable like this, then this variable is a local variable. Goes to the stack. The memory for this particular variable a' will be allocated from the stack frame of the main method. Let's say we want to store an integer in the heap. To reserve or get some allocated space on the heap, we need to call the malloc function, something like this. The malloc function asks how much memory to allocate on the heap in bytes.
When we say malloc and pass the size of an integer as an argument, then we are saying: "Hey, give me a block of memory, which is 4 bytes. 4 bytes is the typical size of an integer. So a 4-byte block would be reserved or allocated on the heap and malloc will return a pointer to the starting address of this block. And malloc returns an empty pointer. Let's say the starting address of this 4 byte block is 200, then malloc will return us the address 200. Now, we have a pointer to the integer p, which is a local variable of main. So, this will be allocated on the stack frame of the main method.
We have done some typecasting here, because malloc returns a pointer to void, sorry, empty pointer and p is an integer pointer. Now, p stores the address of this memory block which is 200. So, we have a memory block in the heap that we want to use to store an integer. Right now, We don't know what is in this particular block of memory. If we want to complete something here, we need to dereference this location using the p pointer and then enter some value. In fact, the only way to use memory on the heap is through references. The whole malloc function does it, looks for some free space on the heap, reserves it or reserves it for you, and returns the pointer to you.
And the only way to access this particular block is by maintaining a pointer variable that will be local to your function. Now, let's write something like this. After writing 10 in this particular block, I'll go ahead and make one more call to malloc. When I make one more call to malloc, one more block of 4 bytes is allocated in the heap and let's say the address is 400 for this block. Now the address that is returned by the second call to malloc, we store this address in the variable p. So what happens is that p now points to address 400.
The next line writes address 20 to this address. We assign one more block and modify the address in p so that it points to this block. The previous block will still be on the heap. We are still consuming this memory, it will not be automatically deleted. At any point in our program, if we finish using any memory block that is dynamically allocated using malloc, we should also delete it, because it is an unnecessary consumption of memory, which is an important resource. So what we should have done here is that once we are done using this particular block of memory at 200, we should have made a call to the free function.
Any memory allocated by malloc is cleared by calling free. And to free, we pass the pointer to the starting address of the memory block. So now with this code, this first memory block will first be cleared and then we will point to another memory address. It is the programmer's responsibility to delete anything from the heap if they have allocated it and no longer need it. So you see, in terms of variable scope, anything allocated on the heap is not automatically deallocated when the function completes like on the stack. And it doesn't need to live for the entire life of the application as a global variable.
We can control when to release something on the heap and when to deallocate something on the heap. Yeahwe want to store an array on the heap, like let's say we want to store an array of integers on the heap, then all we do is make a call to malloc requesting a block of memory equal to the total size of the array. in bytes. So if we want a 20 element array of integers, we will make a call to malloc asking for 20 X size of int, which will be 4 bytes. So what will happen now is that one bit of the continuus byte block for 20 integers will be allocated in the heap and we will get the starting address of the heap.
So, we get the base address of the array. This p will point here, to the base address of this block. And then in our code we can use this, 20 integers like P, P, P and so on. As we know, P is the same as saying value at address P, and P is the same as saying value at address P+1. This is what it means. One more thing, if malloc can't find any free memory blocks, can't allocate some memory on the heap, it returns null. So for error handling we need to know this and write our code appropriately.
Malloc and Free. Using malloc and free is C style code. If we want to write the same code, the same logic in C++, there is not much difference. Instead of using these two functions, malloc and free, we will use two operators: New and Delete. And we will write our code this way. So instead of malloc, we use the New operator here and instead of using free, we use delete here. If we want to allocate an array, we use something like this where we put the size in parentheses here. And, if we want to free an array, we use this particular delete operator and two square brackets, sorry, one square bracket.
With C++, we don't have to do all this typecasting, as malloc returns void and we need to typecast it back to an integer pointer. The New and Delete operators are safe to type. What it means is that they are used with a type and return

pointers

to a particular type only. So here p will get a pointer to an integer only. We will talk about dynamic memory allocation and other library functions in more detail in the next few lessons. So, thanks for watching!

If you have any copyright issue, please Contact