Pointers and dynamic memory - stack vs heap
Summary
TLDRThis script delves into the architecture of memory, explaining the four segments of a program's memory: the instruction, global variable, stack, and heap segments. It clarifies the fixed nature of the first three and the dynamic, expandable nature of the heap, crucial for large data storage and flexible memory management. The tutorial covers the use of dynamic memory in C and C++ through functions like malloc, calloc, realloc, free, and operators new and delete, emphasizing the importance of manual memory management to prevent leaks and overflow.
Takeaways
- 🧠 Memory is a crucial resource in computing, and understanding its architecture is vital for programmers.
- 🏗️ A program's memory is typically divided into four segments: instruction, global variables, function calls and local variables (stack), and heap.
- 📚 The text segment holds the program's instructions, while the global segment stores static or global variables accessible throughout the application's lifetime.
- 📈 The stack segment manages function calls, local variables, and has a fixed size that does not grow during the application's runtime.
- 🔄 The heap segment is a flexible memory pool that can grow and shrink during the application's lifetime, used for dynamic memory allocation.
- 📝 In the provided C program example, the 'main' function and other functions like 'Square of Sum' and 'Square' demonstrate how memory is allocated and managed during execution.
- 🔑 The call stack is a LIFO (Last In, First Out) structure where the most recent function call is at the top and execution pauses until it returns.
- 🚫 Stack overflow occurs when the call stack exceeds its reserved memory, leading to a program crash, often due to infinite recursion.
- 🛠️ Dynamic memory allocation using the heap allows for more flexibility in memory usage, but requires manual management to avoid memory leaks.
- 🔄 In C, memory on the heap is allocated with 'malloc' and deallocated with 'free', while in C++, 'new' and 'delete' operators are used for the same purpose.
- 🔒 It's important for programmers to be cautious with heap memory to prevent overuse and ensure proper deallocation when memory is no longer needed.
Q & A
What are the four segments of memory in a typical architecture?
-The four segments of memory in a typical architecture are the text segment for storing instructions, the global variable segment for static or global variables, the stack for function calls and local variables, and the heap for dynamic memory allocation.
What is the purpose of the stack in memory management?
-The stack is used to store information about function calls, including local variables and where to return after the function finishes executing. It is allocated during runtime and has a fixed size that does not grow during the application's execution.
Why is the heap different from the other memory segments?
-The heap is different because its size can vary during the lifetime of the application. Unlike the stack, the heap does not have a set rule for allocation or deallocation, and a programmer can control the memory usage from the heap as needed.
What is the relationship between the call stack and the stack segment of memory?
-The call stack is a concept that represents the order of function calls during program execution. The stack segment of memory is the physical area where the call stack is implemented, storing local variables and function call information.
What is the potential issue with using a large amount of memory on the stack?
-Using a large amount of memory on the stack can lead to stack overflow if the call stack grows beyond the reserved memory for the stack, causing the program to crash.
How does dynamic memory allocation with the heap differ from memory allocation on the stack?
-Dynamic memory allocation with the heap allows for flexible memory usage where the size can grow and shrink as needed, and the programmer has control over when to allocate and deallocate memory. Memory on the stack is automatically allocated and deallocated with function calls and has a fixed size.
What are the four functions used for dynamic memory allocation in C?
-The four functions used for dynamic memory allocation in C are malloc, calloc, realloc, and free.
What is the purpose of the malloc function in C?
-The malloc function is used to allocate a specified amount of memory on the heap and returns a pointer to the starting address of the allocated block.
What is the significance of the free function in C?
-The free function is used to deallocate memory that was previously allocated with malloc, preventing memory leaks and unnecessary consumption of memory resources.
What are the two operators used for dynamic memory allocation in C++?
-The two operators used for dynamic memory allocation in C++ are new and delete, which are type-safe and simplify memory allocation and deallocation compared to C's malloc and free.
Why is it important to free memory allocated on the heap?
-It is important to free memory allocated on the heap to avoid memory leaks and to ensure efficient use of memory resources. Unlike the stack, memory on the heap does not get automatically deallocated when the function that allocated it completes.
Outlines
📚 Understanding Memory Architecture and Dynamic Memory in C/C++
This paragraph introduces the fundamental concepts of memory architecture, focusing on how operating systems and programming languages manage memory. It explains the division of a program's memory into four segments: the text segment for instructions, the global variable segment for static variables, the stack for function calls and local variables, and the heap for dynamic memory allocation. The explanation includes a simple C program example to illustrate how these segments are used during program execution, particularly highlighting the stack frame and the call stack. The paragraph also touches on the limitations of stack memory, such as fixed size and scope, and introduces the heap as a flexible, programmer-controlled memory segment.
🔄 The Stack and Heap: Limitations and Dynamic Memory Allocation
The second paragraph delves deeper into the stack's limitations, such as the inability to grow during runtime and the risk of stack overflow due to excessive recursion or deep function calls. It contrasts the stack with the heap, which is a dynamic memory segment that can expand and contract as needed. The heap allows for more flexible memory management, enabling the programmer to allocate and deallocate memory as required. The paragraph also clarifies the difference between the heap as a memory segment and the heap data structure, emphasizing that the heap's role in memory management is distinct from its implementation as a data structure.
🛠️ Mastering Dynamic Memory Allocation with malloc, calloc, realloc, and free in C
This paragraph provides an in-depth look at dynamic memory allocation in C, detailing the use of the malloc, calloc, realloc, and free functions. It explains how malloc is used to request memory from the heap and return a pointer to the allocated block, as well as the importance of typecasting the returned void pointer to the appropriate data type. The paragraph also discusses the necessity of manually freeing memory using free to avoid unnecessary memory consumption. It illustrates the process with code examples, demonstrating how to allocate memory for an integer and an array, and the importance of error handling when malloc fails to allocate memory.
🚀 Transitioning to C++: Using new and delete for Dynamic Memory Management
The final paragraph shifts the focus to C++ and its approach to dynamic memory allocation using the new and delete operators. It contrasts this with the C-style functions malloc and free, highlighting the type safety and simplicity of the new and delete operators, which eliminate the need for typecasting. The paragraph provides examples of how to allocate and deallocate memory for a single integer and an array in C++, emphasizing the ease of use and the importance of proper memory management to prevent memory leaks. It concludes with a teaser for upcoming lessons that will cover more on dynamic memory allocation and related library functions.
Mindmap
Keywords
💡Memory
💡Dynamic Memory
💡Stack
💡Heap
💡Global Variables
💡Malloc
💡Free
💡New Operator
💡Delete Operator
💡Stack Overflow
💡Memory Management
Highlights
Memory is a crucial resource for any machine, and understanding its architecture is essential for programmers.
The memory in a typical architecture is divided into four segments: instruction, static/global variables, function calls and local variables, and heap.
Static or global variables have a lifetime equal to the application and are accessible throughout its lifecycle.
Local variables are declared inside functions and only exist during the function's execution time.
The stack frame is the memory allocated on the stack for a method's execution, including local variables and return information.
The size of the stack frame is calculated at compile time, and it cannot grow during runtime.
Heap memory, unlike stack, can vary in size during the application's lifetime and is not subject to fixed allocation rules.
Heap is also known as the free pool of memory or free store, and it can grow as long as system memory permits.
Dynamic memory allocation on the heap is controlled by the programmer and requires careful management to avoid memory wastage.
The term 'heap' in memory management does not refer to the heap data structure commonly studied in computer science courses.
In C, dynamic memory allocation is handled by the functions malloc, calloc, realloc, and free.
C++ offers new and delete operators for dynamic memory allocation, providing type safety and eliminating the need for typecasting.
When malloc cannot allocate memory, it returns null, which is important for error handling in programming.
The scope of variables allocated on the heap does not automatically deallocate when the function completes, unlike on the stack.
Global variables should only be used when needed across multiple functions and throughout the entire program lifecycle to avoid unnecessary memory usage.
Stack overflow can occur if the call stack grows beyond the reserved memory, such as in the case of infinite recursion.
Declaring large data types like arrays as local variables on the stack requires knowing the size at compile time, which can be limiting.
The heap provides a flexible solution for scenarios where the size of data structures needs to be determined at runtime.
Transcripts
Memory, is one important and crucial resource on our machine and it is
always good to know the architecture of memory, the way operating
system manages memory and the way memory is accesible to us as
programmers.
In this lesson we will discuss the concept of dynamic
memory and we will see how to work with dynamic memory using C or
C++.
The memory that is assigned to a program or application in a
typical architecture can be divided into four segments.
One segment of
the memory is assigned to store the instructions that need to be
executed.
Another section stores all the static or global variables, the
variables that are not declared inside a function, and that have the
whole lifetime of an application, they are accesible anywhere during the
whole life cycle of the application as long as the application is running.
One section of the memory is used to store all the information of
function calls and all the local variables and we have also talked about
stack in our lesson on Call by Reference.
Local variables are declared
inside a function and they live only till the time the function is executing.
The amount of memory set aside for these three segements: the text
segment, the global variable segment and the stack, does not grow
while the application is running.
We will come back to why we use this
fourth segment- heap , in a while.
Let us first understand how these
three segments of the memory are used when a program executes.
I
have a simple C program here.
We have a function square that gives me
the square of a number.
We have another function Square of Sum that is
given two arguments x and y, and it returns us the square of x plus y.
And, in the main method, I am just calling this function Square of Sum
passing it two arguments a and b.
Let us now see what happens in the
memory when this program executes.
Let us say this rectangle in green
here is memoryreserved as stack and the rectangle in orange is the
memory reserved as Static or Global variable section.
When the program
starts executing, first the main method is invoked.
When the main
method is invoked, some amount of memory from the stack is allocated
for execution of main.
And this total is a global variable, so it should sit
in the global section.
The amount of memory allocated in stack for
execution of main can also be called the stack frame for the method
main.
All the local varibales, arguments, and the information where this
function should return back to, all this information is stored within this
stack frame.
The size of the stack frame for a method is calculated
when the program is compiling.
Now, when main calls Square of Sum
method, let's write shortcut SOS, for Square of Sum, then a stack frame
is allocated for the call to Square Of Sum, all these local varibales x, y
and z will sit in this particular stack frame.
Now, Sum of Square calls
Square,lets again put a shortcut here for square, 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 rest are kind of paused, waiting for the function above to
return something and then it will resume execution.
I have drawn these
play and pause button here, in case you do not understand.Ok, so this
total is a global variable , it's here in this section.
Global variable
because it is not declared inside a function.
We can access it anywhere,
and then we go to this particular statement where we call Square of
Sum, and Square of Sum is calling Square and so this is called our call
stack.
This program may not be the best way to implement this logic.
I
have written this program this way so that I can have some nested
methods calling each other.
Let's say right now we are at this particular
statement, we are executing this statement.
So, at this stage the call
stack will have these three methods.
Now, when this method finishes,
we will return back to this particular statement.
As soon as Square
function will return, it will be cleared from the stack memory and now
Square of Sum function will resume.
Once again when Square of Sum
finished, the control will come to this particular line total is equal to
Square of Sum and main will resume again.
Now, main will call printf, so
once again printf will go to the top of the stack.
Printf will finish and the
control will come back again to main and now main will finish.
And, now
main finishes, program will also finish.So, In the end, our global variables
will also get cleared.
There was no need in this program to keep this
variable total as global.
We should assign a variable as global only if it
is needed at multiple places in multiple functions and it is needed for
the whole lifetime of the program, otherwise it is a waste of memory to
keep a variable for the whole lifetime of program execution.
We had kept
one global variable in this program just to understand the concepts.
Here, I must point out one more thing, when our program starts, our
operating system allocates some amount of reserved space, let's say OS
allocates 1 MB of space as stack, but the actual allocation of the stack
frame and the actual allocation of the local variables happens from the
stack during runtime and if our call stack grows beyond the reserved
memory for the stack like for example, if a method A calls B, B calls C
and we go on calling and we exhaust the whole space reserved for the
stack, then this is called stack overflow and in this case our program will
crash.
One common case of stack overflow is when you write a bad
recursion and your program goes infinitely into recursion.
So, as we can
see, there are some limitaions of stack.
The memory set aside for stack
does not grow during runtime.
Application cannot request more memory
for stack.
So, if it is 1 MB, then if the allocation of variable and functions
in stack exceeds 1 MB, our program will crash.
Further the allocation and
deallocation of memory onto the stack happens by a set rule.
When a
function is called, it is pushed onto the stack, when it finishes, it is
popped out of 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
local variable, then we need to know the size of the array at compile
time only.
If we have a scenario like we have to decide how large the
array will be based on some parameter during runtime then it is a
problem with stack.
For all these problems, like allocaing large chunks of
memory or keeping variable in the memory till the time we want, we
have heap.
Unlike stack, application's heap is not fixed.
It's size can vary
during the lifetime of the application and there is no set rule for
allocation or deallocation of the memory.
A programmer can totally
control how much memory to use from the heap, till what time to keep
the data in the memory during the applications lifetime and heap can
grow as long as you do not run out of memory on the system itself.
That
is a dangerous thing also and we need to be really careful about using
heap for this reason.
We also sometimes call heap, free pool of memory
or free store of memory.
We can get as much as we want from the heap.
How heap is implemented by the operating system, language runtime or
the compiler, is something which can vary, which is a thing of computer
architecture.
But an abstracted way of looking at the heap as a
programmer is that this is one large free pool of memory available to us
that we can use flexibly as per our need.
Heap is also called dynamic
memory and using the heap is referred to as dynamic memory allocation.
Let us now see how to use the heap in out C or C++ program.
I will clear
this code in the left and I will draw one more rectangular block here for
heap.
there is one more thing that I must point out before moving
forward.
Heap is also one data structure and if you do not know about
this data structure Heap yet, you will learn about it in your Data
Structure course.
But this nomenclature here has nothing to do with
heap data structure.
The term heap is being used only for the large free
pool of memory.
Heap data strcutre does not come anywhere in this
context.
This term often confuses a lot of people when they know about
heap data structure.
Stack is also one data strcutre but the stack
segment of the memory is actually an implementation of the stack data
structure but heap is not an implementation of the heap data structure.
To use dynamic memory in C, we need to know about four functions
malloc, calloc, realloc and free.
To use dynamic memory in C++, we need
to know about two operators new and delete.
These four functions can
also be used in C++, as C++ has backward compatibility.
It is only a
superset of C. But C++ programmers use mostly these two operators,
new and delete.
We will see some code examples and try to understand
how things happen when dynamic memory is used.
Let us first pick up
some code examples in C. Let us write a C program.
I will clean up some
of the stuff in the right.
1 MB for stack, this was just an assumption.
In
reality, the size of the stack will be decided by the operating system and
the compiler.
It is a thing of computer architecture.
Coming back to the
code, if we declare a variable like this, then this variable is a local
variable.
It goes on the stack.
Memory for this particular variable a' will
be allocated from the stack frame of the main method.
Let us say we
want to store an integer on the heap.
To reserve, or get some space
allocated on the heap, we need to call the malloc function, something
like this.
The malloc function asks for how much memory to allocate on
the heap in bytes.
When we say malloc and pass as argument size of
integer, then we are saying that "Hey, give me a block of memory, which
is 4 bytes. 4 bytes is the typical size of the integer.
So, one block of 4
bytes will be reserved or allocated on the heap and malloc will return a
pointer to starting address of this block.
And, malloc returns a void
pointer.
Let us say, the starting address of this block of 4 bytes is 200,
then malloc will return us address 200.
Now, we have a pointer to
integer p, which is a local variable to main.
So, this will be allocated in
the stack frame of the main method.
We have done a typecasting here,
because malloc returns pointer to void, sorry, void pointer and p is an
integer pointer.
Now, p stores the address of this block of memory which
is 200.
So, we have got some block of memory on the heap which we
want to use to strore an integer.
Right now, we do not know what's there
in this particular block of memory.
If we want to fill in something here,
we need to dereference this location using the pointer p and then put in
some value.
In fact the only way to use memory on heap is through
reference.
All the malloc function does it, looks for some free space in
the heap, books it or reserves it for you and give back the pointer.
And
the only way you can access this particular block by keeping a pointer
variable which will be local to your function.
Now, let us write something
like this.
After writing 10 in this particular block, i will 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 on the heap and let us 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 is now pointing to the address 400.
The next line writes
address 20 to this address.
We allocated one more block and we
modified the address in p to point to this block.
The previous block will
still sit in the heap.
This memory we are still consuming, it will not be
cleared off automatically.
At any point in our program , if we are done
using some block of memory which is dynamically allocated using
malloc, we also need to clear it, because it is unnecessary consumption
of memory which is an important resource.
So, what we should have
done here is that once we were done using this particular block of
memory at 200, we should have made a call to the function free.
Any
memory which is allocated using malloc, is cleared off 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 block of memory will first be
cleared and then we will be pointing to anohter memory address.
It is
the responsibility of the programmer to clear anything on the heap if he
has allocated it and does not need it any further.
So, you see, in terms of
the scope of the variable, anything allocated on the heap is not
automatically deallocated when the function completes like on the stack.
And, it does not need to live for the whole lifetime of the application
like a global variable.
We can control when to free anything on the heap,
when to deallocate anything on the heap.
If we wanted to store an array
in the heap, like let's say we wanted to store an integer array into the
heap, then all we do is make a call to the malloc asking for one block of
memory equal to the total size of the array in bytes.
So, if we want an
integer array of 20 elements, then we will make a call to malloc asking
20 X size of int which will be 4 number of bytes.
So, what will happen
now, is that one bit of contigous block of byte for 20 integers will be
allocated on the heap and we will get the starting address of the heap.
So, we kind of 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 as P[0], P[1], P[2] and so on.
As we know, P[0] is same as
saying value at address P, and P[1] is same as saying value at address
P+1.
This is what it means.
One more thing, if malloc is not able to find
any free block of memory, is not able to allocate some memory on the
heap, it returns null.
So, for error handling, we need to know this and we
need to write our code appropriately.Malloc and Free.
The use of malloc
and free is C style code.
If we want to write the same code, 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 something like this.
So, instead of
malloc, we are using the New operator here and instead of using free,
we are using delete here.
If we want to allocate an array, we use
something like this where we put the size in brackets here.
And, if we
want to free an array, we use this particular operator delete and two
brackets, sorry, one bracket.
With C++, we do not have to do all these
typecastings, like malloc returns void and we need to typecast it back to
integer pointer.
New and Delete operators are type safe.
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 integer only.
We will be talking
about dynamic memory allocation and other library function in more
detail in the coming lessons.
So, Thanks for watching!
Browse More Related Video
![](https://i.ytimg.com/vi/UOB7-B2MfwA/hq720.jpg)
SMART POINTERS in C++ (std::unique_ptr, std::shared_ptr, std::weak_ptr)
![](https://i.ytimg.com/vi/9LaB6wbZepg/hq720.jpg)
Life Cycle & Reference Counting | Godot GDScript Tutorial | Ep 1.2
![](https://i.ytimg.com/vi/cEQveEfPVtc/hq720.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-BIAC4AOKAgwIABABGGUgSChDMA8=&rs=AOn4CLB3t-A2xX3s3X8mWL05Z9zhKPTyYQ)
COS 333: Chapter 12, Part 2
![](https://i.ytimg.com/vi/Vd-4eGnBAK8/hq720.jpg)
The What, How, and Why of Void Pointers in C and C++?
![](https://i.ytimg.com/vi/Ty4O74t7koM/hq720.jpg)
Arrays & Dynamic Memory Address | Godot GDScript Tutorial | Ep 10.1
![](https://i.ytimg.com/vi/fibH4WQWkRA/hqdefault.jpg?sqp=-oaymwEXCJADEOABSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCVSYJ5_g3hIK_7Grr-QqNZY0fycQ)
COS 333: Chapter 5, Part 1
5.0 / 5 (0 votes)