Q- How are variables accessed within a frame in the stack?
Ans: Well as there is a pointer known as stack pointer to manage different operations on stack, the same way there is a frame pointer to access different variables with a frame. The stack pointer points to the top of the stack while the frame pointer points to the starting of the topmost frame as shown:
Now when the new function is called, the new frame is added in the stack and the frame pointer of the previous frame is saved in this new frame and the frame pointer now contains the starting address of the new frame as shown:
NOTE: We have represented this frame pointer as part of OTHER REGISTERS.
Similarly if now we have a new function call, then new frame is added in stack and frame pointer of frame ‘abc’ is stored in this new frame and the frame pointer now contains the address of this new function.
How ever when the function returns, the frame address stored in the frame is re-loaded in the Frame Pointer.
Hence Frame Pointer always contains the starting address of the active frame and active frame always contains the frame address of the lower frame.
int main()
{
int*x;
x = abc();
printf(“%d”,*x);
return 0;
}
int* abc()
{
int y=3;
return (&y);
}
As address of the variable y is returned and which gets stored in x and the memory block of function abc() is de-allocated.
Next we try to store a value in the memory which has been de-allocated. As memory has been de-allocated and hence operating system may have allocated this memory to some other process. Due to this reason referencing to this address may cause an ERROR and hence we better should mark the option of uncertain or can’t say.
SOLUTION: The solution to the above problem can be that if we declare a pointer in place of simple variable and return the address stored in the pointer as:
int main()
{
int*x;
x = abc();
printf(“%d”,*x);
return 0;
}
int* abc()
{
int y=3;
return (&y);
}
(a) 3 (b) 5 (c) Can’t say
Ans: Many would answer it as 3 but it is wrong. The way we have tried to do it is wrong. As we enter into main we have a memory allocated for variable x and then function abc is called.
Now as function abc() is called, a new frame is added in the stack. We save the return address to main in the stack. Then a variable y is declared.
int main()
{
int x=3;
fun(x);
return 0;
}
void fun(int n)
{
n--;
if (n!=0)
{
fun(n);
}
}
Explain the allocation of memory in stack for the above program.
Ans: firstly a frame in stack is created and memory is allocated to store variable x with value 3.
And then function call is made to function fun() and a new frame is created in the stack. Firstly return address to main is saved in the stack. Next memory is allocated for variable n and 2 is stored in n.
Now n—is executed and n becomes 1. Then if statement is executed and another call to fun() is made and value 1 is passed. Again return address to function Fun() is saved and then value 1 is stored in variable n. execution of n- – decreases the value to 0.
Q-Why do sometimes we encounter an error named as ‘Stack Overflow ERROR’?
Ans: It occurs due to the infinite recursion calling. As we can see in the above case that in case of recursion (when a function calls itself), everyt ime a function is called new memory is allocated in the stack and hence in case of infinite incursion, a function is called infinite times and hence memory is allocated infinite times and hence we fall in shortage of memory in stack which is STACK OVERFLOW.
Now we have the next statement as if statement whose condition goes wrong as value of n is zero and hence no function call is made. After this we face a closed parantesis which means function is returned to the address saved in the address register.
Now we are in the function Fun() which was called first. Next statement in this function is also a closed parenthesis. Hence function again gets returned.
Now the main ends and program execution ends and hence whole stack is de-allocated and whole stack becomes empty
int a = 2;
int main()
{
int x= func2(5);
printf(“%d”, x);
return 0;
}
int func1(int n)
{
int p;
p= a*n*n;
printf(“%d”, p);
return 3;
}
void func2(int n)
{
int k;
func1(n) ;
k=func1(2*n);
printf(“%d”,k);
return 7;
}
Now we have to picturize the whole scenario in HEAP and STACK memory.
Ans: We have no dynamically allocated variables in this program. Hence there is not much role of HEAP memory. We analyze the memory allocation and de-allocation in STACK on next page. We know stack memory is used to store local variables and the return address pointer, frame pointer (the pointer which is used to point to the starting of the frame just below) and other registers.
The global variable ‘a’ with value 2 is stored in the fixed memory and the memory remains allocated till the program execution ends.
Now as we enter into main, a frame is created in the stack. Next with the statement int x= func2(5), memory is reserved for the local variable x in the stack.
Now we have a function call to function func2 hence a new frame is created and firstly return address to main is stored in the stack. Next we have to allocate a space for variable n which is a parameter of the function func2() and 5 is stored. After this another local variable k is allocated space in stack with random value.
Now we make a function call to func1(). Hence a new frame is created in the stack and n=5 is passed as parameter. Firstly return address to the function func2() is saved on stack. Next space is allocated to variable n and given value 5. This variable n is different from the variable n in the function func2() although they have same names but different addresses. Now we have the statement int p; i.e. again memory is allocated for another local variable p and random value is there in the memory. After this the statement p=x*n*n evaluates tostore a value 2*5*5 = 50.
After this value of p is printed and value 3 gets returned. As the function func1 ends & returns to the address stored in the address register and hence its frame is de-allocated memory and memory map of stack is as:
Now we are in the function func2(). Next statement we have is k=func1(2*n); Hence a function call to func1 is made with parameter 2*5=10 and a new frame is created. In the func1 function firstly return address to func2 function is saved along with other registers. Next space is allocated to variable n and given value 10. This variable n is different from the variable n in the function func2() although they have same names but different addresses. Now we have the statement int p; i.e. again memory is allocated for another local variable p and random value is there in the memory. After this the statement p=x*n*n evaluates tostore a value 2*10*10 = 200.
Now value of p is printed and 3 gets returned and stored in variable k in function func2.
After this value of k gets printed and value 7 is returned. The function func2 returns to main and 7 gets stored in x.
After this value of x is printed and program ends. And hence the whole stack is de-allocated and even global variable ‘a’ is de-allocated.
Now we have the following statements and we see how STACK & HEAP memory is allocated to different variables. We have the empty HEAP memory & STACK memory as:
NOTE: For this question we have used a heap memory & allocated space serially while in real, memory is allocated randomly by operating system.
char* x;
x= (char*)malloc(2);
char* y= (char*)malloc(4);
free(x);
char* z= (char*)calloc(3, sizeof(char));
free(y);
free(z);
Now we represent the HEAP and STACK memory after each statement.
char* x;
Assuming X would occupy 4-bytes for storing address. Hence 4 bytes are allocated for x in the STACK but only some random value that is already preset at the allocated memory gets stored in the pointer.
x= (char*)malloc(2);
With this instruction, firstly 2 bytes are allocated from the HEAP and then the starting address of the allocated memory is stored in the pointer x but the values stored in the memory allocated are random. Now note that in the pointer there is no information stored about where the allocated memory ends. So how would we know where the memory ends while accessing memory? Actually we can’t know the end. The programmer has to take care of himself otherwise it’ll cause ERROR. While storing characters a NULL character ’\0’ is stored to detect the end of memory.
Char* y= (char*)malloc(4);
Similar to the above case, 4 bytes are allocated for the pointer in the STACK and then the 4 bytes are allocated from HEAP and starting address is stored in the pointer.
free(x);
Now there ia no memory allocated to the pointer x and hence if now we try to use the memory then it may cause ERROR as we’ll referencing to the deallocated memory. Now one may ask that what the problem in referencing that memory is. Actually as we have de-allocated the memory, the operating system can allocate that space to some other proess and then if we refer to that space then it may cause a serious ERROR. HOW CAN WE AVOID THIS PROBLEM? We can avoid this problem by putting NULL to the pointer after de-allocating the memory as shown:
free(x);
x= NULL; So that now if we mistakenly try to refer to the memory then it causes no serious problem.
Char* z= (char*)calloc(3, sizeof(char)); SIZE OF CHAR WE TAKE AS 1 BYTE.
calloc() function is same as malloc() but it also initializes the allocated memory to zero. Hence 3 bytes are allocated from the HEAP memory and zero (0) is stored in the allocated memory 2006 – 2008 and starting address is stored in the pointer z.
free(y);
The memory allocated to pointer y is freed and hence that memory is available for re-use.
free(z);
The memory allocated to pointer z is freed and hence that memory is available for re-use.
The STACK memory would be freed when the function containing the above statements returns or if statements are in the main () function, then the memory in the STACK would be freed when the program ends.
The functions to manage heap memory which are included in the directory #include <stdlib.h> are:
malloc():
void * malloc (size_t size);
As we see from the prototype of the malloc function that we have to pass the number of bytes required as parameter to the malloc function and the function returns the starting address of the allocated apace as void pointer. As the void pointer is returned hence we have to typecast the pointer as the type we require. E.g.
If we need 6 bytes for intergers we type cast the memory as
Int* x;
x=(int*)malloc(6);
If we are storing char variables in the allocated space then we do as:
Char* y;
y=(char*)malloc(6);
calloc():
The prototype of the function is as:
void * calloc (size_t nr, size_t size);
nr is number of variables of the required type and size is the number of bytes required for that type
and this function automatically initializes the memory allocated to zero.
e.g.
if we need memory for 6 integers
int* x;
x=(int*)calloc (6, sizeof(int) );
if we need memory for 6 characters
char* x;
x=(char*)calloc (6, sizeof(char) );
NOTE: If any of the two functions is not able to allocate memory then it returns a NULL pointer.
realloc():
The prototype of the realloc() function is
void * realloc (void *ptr, size_t size);
This function is used to redefine the size already allocated. Suppose as I have already allocated memory for 6 integers in the previous example, now I want the space for the 8 integers then I do as:
This article is very important for the person who wants to understand c language. Anyone who is going for any interview for a good software company must read the following article as the questions asked in the tests and interviews need the candidate to be clear in the following concepts. This article would ensure that the answers to the following questions are answered properly:
How are variables and pointers actually represented at the machine level?
What is the reason behind the dangling pointers?
Why do the dereference error occurs?
Why do we have other bugs on pointers?
Hence I recommend every person who wants to have a good understanding to read this article. I have explained concepts by transforming them into the form of questions. The article has been written in such a way that it becomes very simple for the user to understand this.
In C language we have different types of variables like global variable, local variable, parameter to the function and dynamically allocated variable.
But do we know how memory is allocated to these variables and where are these variables stored?
This concept is very important to understand. So let’s discuss this and understand the language better which would help us in dealing with many errors in the program.
We consider 3 kinds of memory in a computer system
FIXED MEMORY:
This memory is used to store the program written or the application being run (i.e. the executable code) and the constant variables or other constant structures. The global and the static variables are also stored in the fixed memory. This memory is categorized into code memory, data memory etc. The code memory stores the executable code while the data memory stores the global and the static variables.
STACK MEMORY:
The stack memory is a data structures which follows the policy of Last In First Out (LIFO).The stack memory is used to store the static data which includes the variables which are declared at the start of the program like the fixed arrays, local variables and the parameters which are passed to a function during the function call. Other than this the return address pointer, frame pointer (the pointer which is used to point to the starting of the frame just below) and other registers. The life of the static variables ends as soon as the function returns or the program execution stops. The stack memory is used separately for separate programs in an operation system. The memory in stack is allocated at the compile time.
Disadvantage: The memory declared first remains the same i.e. if we declare large memory then there is wastage of memory while if we declare lesser memory then we are in a problem.
Advantage: The memory allocated is deallocated automatically after the function returns and hence the same memory is available for re-use again.
HEAP MEMORY
The heap memory is used to allocate space to the dynamic data. The data whose size can shrink and expand as the requirements vary. We can allocate the extra memory whenever required using command such as malloc(), calloc(), realloc() and de-allocate the space already allocated using command such as free(), delete(). The memory in heap is allocated at the run time.
Wecan take HEAP memory as a group of nodes (circles) and every node is reachable only if It has a direct or indirect reference from some root node (pointer) which is stored in STACK memory. When we use memory allocation functions like malloc, calloc or realloc the memory node are selected and linked to the specified root node. Now if we don’t free the nodes of HEAP memory before the root node is de-allocated, the reference to those memory nodes is lost and they become unreachable, hence known as garbage memory or memory leak. Hence to avoid this problem the programmer must free the memory after it’s use.
Advantage: This memory can be anytime shrunk or expanded when ever the requirements of the program changes.
Disadvantage: We have to manually deallocate the memory allocatd using function like free or delete, otherwise the memory which is not deallocated at the proper time goes to the garbage and is not available for re-use.
We can represent the above memories in a diagram as
Example
Consider the program
int x=4;
main()
{
int z;
char* y;
y= malloc(6);
y = "Hello";
printf(“%c”, *y);
z = square(x);
printf(“%d”, z);
free(y);
}
int square(Int k)
{
return k*k;
}
Now we see which variables in the program are stored where.
In this program we have different types of variables like x as global variable, z & k as local variable and as parameter to the function and y is dynamically allocated variable. So x is stored in fixed memory, z & k are stored in Stack Memory and the pointer y is also stored in stack memory while the 6 bytes (allocated by malloc) which are pointees of pointer y are stored in heap memory.
It is the simplest pointer. The NULL pointer is a pointer which points to no where.. We can also use integer 0 in place of constant NULL.
Int* x=NULL;
The above statement creates a NULL pointer x.
DEREFERENCING NULL
As we know NULL pointer is actually a pointer which points to nowhere and hence we can not dereference to the NULL pointer. It is a run time ERROR to point to the NULL pointer.
Int* x=NULL;
*x=3; DEREFERENCING NULL ERROR
After execution of statement 1, in the memory map x can be described as follow:
Now if we try to execute statement2, which actually is trying to assign value to the pointee of the ponter x. But as we know, the above declared pointer has no pointee, hence now we can recognize an ERROR in the above set of statements and the ERROR is called DEREFERENCING NULL
DANGLING POINTER:
The dangling pointer ERROR is an error in which we try to dereference to a memory place which has not been allocated yet.
Int* x;
*x=3; DANGLING POINTER ERROR
After execution of statement 1, in the memory map x can be described as follow:
As till now we have allocated no pointee to the pointer and still we are try to dereference. As there is a random address stored in the pointer, hence if we dereference it now then we may dereference a wrong memory and hence may even corrupt some crucial application.
How ever if we only try to print the value *x then there are no chances of corrupting any application and hence it would not cause much harm but still an erroneous output. It is also called bad pointer bug.
REFERENCING DE-ALLOCATED MEMORY:
This problem occurs when the address of the local variable is returned from a function or the programmer deletes the allocated memory mistakedly.
main()
{
Int *y=abc();
*y=3; A SERIOUS BUG (You’ll get the full explanation in the article Memory Management)
}
Int* abc()
{
Int x=7;
return (&x);
}
After the execution of statement1 in the main, the memory map is as:
Now we have reached the function abc (), the execution of statement1 of function abc() results into following memory map:
But when the function abc() returns, the address of the variable x is stored in y and INTENTION is to use x as pointee of y. But with the return of the function abc(), the stack frees the variable x. Hence memory location of x can be used somewhere else by the Operating System.
The non-belonging memory is the memory which doesn’t belong to the program anymore. Hence the situation now is similar to the DANGLING POINTER case. Hence the 2nd statement of main() may cause ERROR.
Note: REFER TO THE ARTICLE MEMORY MANAGEMENT FOR BETTER UNDERSTANDNG