Sitemap

Misc Part 1 (C Program Execution and Program Memory Layout)

8 min readApr 6, 2020

--

https://www.youtube.com/watch?v=cJDRShqtTbk

Various Stages Of Program Execution

Starting from the code, The actual code is given to the compiler then it is passed to the preprocessor and the preprocessor will produce a pure C code then it actually compile and produces the assembly code . Then assembly code is send to the assembler which is also a compiler that convert assembly code into machine code. But object file have some unresolved symbols and function such as printf the compiler only verify printf function follows the correct prototype it would not bother about the actual implementation of printf function exists or not. Here comes the need of Linker. Linker is the utility program which resolves the unresolved symbols and functions and combine the multiple object file into a single executable. Compiler can generate multiple object file may be unresolved symbol from an object file can reside in another object file. See in example unresolved symbol X in second object file can reside in first object file here linker will resolve X by linking it with first object file and create a one executable. When we execute the program the loader will read the instruction from the executable file and loads it into the RAM. Finally the CPU initiates a program execution by calling the starting method

https://www.youtube.com/watch?v=cJDRShqtTbk

Preprocessor

When we write C program . the C program first process by Preprocessor and preprocessor generates some intermediate file that file given to compiler. These are the responsibilities of Preprocessor

  1. Remove comment
  2. It will include header file code in that file and as shown in Figure 1 it will copy studio.h whole code in our file. Angular brackets < and > instruct the preprocessor to look in the standard folder where all header files are held. Double quotes and instruct the preprocessor to look into the current folder (current directory).
  3. Replace all its macro name with the value , Replace Macro name with code. In a C program, all lines that start with # are processed by preprocessor which is a special program invoked by the compiler. In a very basic term, preprocessor takes a C program and produces another C program without any #.
  4. When we use define for a constant, the preprocessor produces a C program where the defined constant is searched and matching tokens are replaced with the given expression.
Figure 1

Compiler

Takes a C program from preprocessor and convert it into assembly code

Figure 2

Assembler

Convert Assembly code into binary object code

Figure 3

Difference between Compiler and Assembler

Compiler → Convert actual code into assembly code

Assembler → Convert assembly code into machine / object code

Pointers and Dynamic variable stack vs heap

Memory is crucial resource and it’s good to know the architecture of memory, how OS operate and how memory accessible to a programmer. When the program is loaded into the RAM, the structure of that allocation of the program into the RAM is what we talking Program Memory. Memory allocated to typical program can be divided into mainly four segments.

  1. Heap
  2. Stack → Stack memory don’t
  3. Static/Global
  4. Code (Text)

Stack

  1. This section of memory is used to store all the information of function calls and all the local variables
  2. Local variables are declared inside a function and they live only till the time function is executing
  3. The memory allocated to this section does not grow while the application is running
  4. Deallocation of the memory is the responsibility of the compiler .
  5. Stack memory is actually the implementation of stack data structure
  6. Stack size is decided by the compiler and the OS
#include <stdio.h>
int total;
int square(int x) {
return x*x
}
int squareOfSum(int x, int y) {
int z = square(x+y)
return z
}
int main()
{
int a = 4
int b = 8
int sum = squareOfSum(a,b)
printf(“%d”,sum)

return 0;
}

Let’s see what happens in memory when this code gets executed. When program executes its main method will execute first . When main method is invoked some amount of memory is allocated from stack. The memory allocated in stack for the execution of main can also be called the stack frame for the method main. All the local variables, 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 is calculated when the program is compiling. Now when main calls squareOfSum(SOS) method then the stack frame is allocated for the call of squareOfSum method. All its local variable will sit in its stack frame. Now squareOfSum calls square and so on

Figure 4

At anytime during the execution of program, the function is at the top of the stack is executing and rest are kind of paused , waiting for the function above return. Important Now when this method finishes it will return As soon as square function return it will cleared from the stack memory(mean all its local variable is deallocated) and now squareOfSum will resume and so on and when man finishes program also finishes and in the end global variable will be cleared

stack OverFlow → Let’s say OS allocate 1MB of space for the stack memory but the actual allocation of the stack frame and the actual allocations of local variables happens from the stack happens at run time and if our call stack grows beyond the reserved memory for example A calls B, B calls C and so on and we exhaust all space reserved for the stack then this is stack OverFlow. and in this case our program will crash. One common case of this is baddd recursion

Static/Global

  1. All the global and static variable stored in this memory location (Both global and static remains in the memory for the whole life time of a program)
  2. The memory allocated to this section does not grow while the application is running

Heap

  1. Unlike stack application heap is not fixed
  2. It’s size can vary during the lifetime of the application
  3. Developer responsibility to free memory
  4. A programmer can totally control how much to use from the heap , till what time to keep the data in memory during the application lifetime and heap can grow as long as you do not run out of memory on the system itself
  5. Also called free pool of memory / free store of memory/ dynamic memory / dynamic memory allocation
  6. Different language have different implementation of heap architecture
  7. Heap memory is not the implementation of heap data structure . Heap data structure is not same as heap memory

Let’s us write a C program, memory of the variable a will be store in stack . Let’s say we want to store an integer on heap , to reserve and get some space allocated on heap, we need to call malloc function. The malloc function asks for how much memory to allocate on the heap in bytes. When we say malloc and pass the size of integer as argument . We are saying give me 4 bytes for memory from heap and malloc will return a pointer to the starting address of this block. Lets say starting address is 200. Now we have a pointer to integer p which is a local variable to a which is main . Now p stores the address of this block. If we want to fill in here we need to dereference this location using the pointer p . The only way to use memory in heap is by reference.

int main()
{
int a = 4
int *p,
p = malloc(size(int))
*p = 10
return 0;
}

By doing this , we allocated one more block . The previous block will still sit in heap as shown in Figure 5. This memory still we consuming , it will not cleared automatically. At any point in our program, if we are done using some block of memory which is dynamically allocated , we also need to clear it

int main()
{
int a = 4
int *p,
p = malloc(size(int))
*p = 10
p = malloc(size(int))
*p = 20
return 0;
}
Figure 5

As below when we done memory we should call free. By doing this we first free 200 address memory then we allocate new memory and assign to pointer p.

int main()
{
int a = 4
int *p,
p = malloc(size(int))
*p = 10
free(p)
p = malloc(size(int))
*p = 20
return 0;
}

Code (Text) Memory:

  1. The memory is assigned to the instructions that need to be executed
  2. When we compile below program to proceed further and get an executable. It loads the function main into RAM and starting address given . Now the next function that we have is add so add gets some memory location as well. There are few things to note the memory location where the program starts is given by a main and that will be lowest memory address between these two function . Now this section in which the program memory is allocated is called Code/Text segment
  3. Thing to note here the memory gets allocated in an increasing order so what happens if we have more functions like add and lets we want another function subtract that would call after this then it would have added another more memory for this subtract and kept on adding on top. These functions are stored in code/text segment
  4. The memory allocated to this section does not grow while the application is running
int main()
{
printf(“Hello World”);
add(1,1)
return 0;
}
int add(int a , int b ) {
return a + b
}

Swift Value Types

Swift value types are kept in the stack, thus there is no dynamic memory allocation, and as a consequence no room for memory leaks. Stack offers automatic deallocation of value types variables after method returns.

Useful Links

--

--

Ali Akhtar
Ali Akhtar

Written by Ali Akhtar

Senior iOS Engineer | HungerStation | Delivery Hero

Responses (1)