Memory Layout of C program

Memory Layout In C

The memory layout of C program mainly comprises five segments these are the stack segment, heap segment, BSS (block started by symbol), DS (Data Segment), and text segment. But you should remember actual memory layout in C might be more complex because C program memory layout is specific to the processor, development tools, and the underlying hardware.

Before explaining the memory layout of C code, I also want to let you know that each segment of memory layout in C has its own read, write, and executable permission. If a program tries to access the memory in a way that is not allowed, then a segmentation fault occurs.

A segmentation fault is a common problem that causes programs to crash. A core file (core dumped file) is also associated with a segmentation fault that is used by the developer to find the root cause of the crash (segmentation fault).

So, let’s get understand the generalized typical memory organization in a C program without wasting the much time.

 

Typical memory Layout of C Program:

Note:  You must remember that this is only an example. The actual static memory layout is specific to the processor, development tools, and the underlying hardware.

1. Stack
2. Heap
3. BSS (Uninitialized data segment)
4. DS (Initialized data segment)
5. Text

High Addresses ---> .----------------------.
                    |      Environment     |
                    |----------------------|
                    |                      |   Functions and variable are declared
                    |         STACK        |   on the stack.
base pointer ->     | - - - - - - - - - - -|
                    |           |          |
                    |           v          |
                    :                      :
                    .                      .   The stack grows down into unused space
                    .         Empty        .   while the heap grows up. 
                    .                      .
                    .                      .   (other memory maps do occur here, such 
                    .                      .    as dynamic libraries, and different memory
                    :                      :    allocate)
                    |           ^          |
                    |           |          |
 brk point ->       | - - - - - - - - - - -|   Dynamic memory is declared on the heap
                    |          HEAP        |
                    |                      |
                    |----------------------|
                    |          BSS         |   Uninitialized data (BSS)
                    |----------------------|   
                    |          Data        |   Initialized data (DS)
                    |----------------------|
                    |          Text        |   Binary code
Low Addresses ----> '----------------------'

 

Stack:

  • The stack memory segment is used to store local variables, function parameters, and bookkeeping information related to function call. When a function is called, a block is reserved on the top of the stack for local variables and some bookkeeping data.
  • Stack memory segment follow the LIFO (Last-In-First-Out) structure, that means the last item pushed onto the stack is the first one to be removed. It also depends on the computer architecture that stack grows down to the lower address or not; Also, when functions are called and return, their corresponding stack frames are pushed and popped from the stack.
  • Stack grows in the direction opposite to heap.
  • A stack frame is created in the stack when a function is called and destroyed when the function returns. It also known as an activation record or activation frame. Stack frame holds information about a function’s execution and its temporary and local variables. Let’s consider an example for better understanding,
void aticleworld(int x, int y, int z)
{
    int C;
    // Some operations
}

In the above example, when function aticleworld() is called, a stack frame for aticleworld() is created. Within this stack frame, memory is allocated for the function parameters x, y, z and for the local variable C. This stack frame also stored the return address to the calling function and any necessary register values that need to be preserved across the function calls.

  • Each function call in a program creates its own stack frame that allows different instances of the same function (called separately or recursively) do not interfere with each other’s variables or execution contexts. For better understanding consider the below example code,
int fact(int a)
{
    if (a <= 1)
    {
        return 1;
    }
    else
    {
        //recursive call of function
        return (a* fact(a- 1));
    }
}

In the above example, each recursive call to function fact() creates its own stack frame. And each call to function has its own set of parameters and local variables. That means in each calling of fact() has its own local variable “a” that does not interfere with the “a” of other instances of the function fact(). It ensures the isolation between another instance of fact().

  • SP (Stack pointer) register keeps track of the top of the stack. The SP helps manage the stack efficiently by pointing to the current top of the stack. Its value change when push/pop actions are performed on the segment.

Heap:

  • Heap memory segment is used to allocate the memory at run time. That means it allows dynamic memory allocation and deallocation during runtime.
  • The heap area is managed by the memory management functions like malloc, calloc, free, etc which may internally use the brk() and sbrk() system calls to adjust its size.
  • The Heap area is shared by all shared libraries and dynamically loaded modules in a process.
  • You must manage the heap memory carefully to prevent memory leaks and another issue related to dynamic memory allocation.
  • It grows and shrinks in the opposite direction of the stack.
#include <stdio.h>

int main(void)
{
    char *pStr = malloc(sizeof(char)*4); //stored in heap

    return 0;
}

 

You can also see below articles,

 

Uninitialized data segment:

  • An uninitialized data segment is also known as BSS (b​lock started by symbol).
  • Uninitialized data segment contains all uninitialized global and static variables.
  • All variables in this segment initialized by the zero (0) and pointer with the null pointer.
  • The program loader allocates memory for the BSS section when it loads the program.
#include <stdio.h>

int data1; // Uninitialized global variable stored in BSS

int main(void)
{
    static int data2;  // Uninitialized static variable stored in BSS

    return 0;
}

 

Initialized data segment:

  • The initialized data segment, also known as the data segment; it contains the explicitly initialized global and static variables.
  • Initialized data segment is distinct from the BSS (Block Started by Symbol) segment.
  • The size of this segment is determined by the size of the values in the program’s source code and does not change at run time.
  • Typically, initialized data segment has read-write permission so the value of the variable of this segment can be changed at run time.
  • This segment can be further classified into an initialized read-only area and an initialized read-write area.
#include <stdio.h>

int data1 = 10 ; //Initialized global variable stored in DS

int main(void)
{
    static int data2 = 3;  //Initialized static variable stored in DS

    return 0;
}

 

Text:

  • The text segment contains a binary of the compiled program.
  • The text segment is a read-only segment that prevents a program from being accidentally modified.
  • It is sharable so that only a single copy needs to be in memory for frequently executed programs such as text editors etc.

 

Note: The size command basically lists section sizes as well as total size for the input object file.

 

Let see few examples to understand the memory layout of the C program.

#include <stdio.h> 
  
int main(void) 
{ 
    return 0; 
}
[aticleworld@CentOS]$ gcc memory-layout.c -o memory-layout
[aticleworld@CentOS]$ size memory-layout
text       data        bss        dec        hex    filename
960        248          8       1216        4c0    memory-layout

 

  • Now add a static uninitialized variable and check the size.
#include <stdio.h> 
    
int main(void) 
{ 
    static int data; // Stored in uninitialized area
    return 0; 
}
[aticleworld@CentOS]$ gcc memory-layout.c -o memory-layout
[aticleworld@CentOS]$ size memory-layout
text       data        bss        dec        hex    filename
960        248          12       1216        4c0    memory-layout

You can see the size of the .bss has been increased.

 

  • Now add the initialized static variable and check the size.
#include <stdio.h> 
    
int main(void) 
{ 
    static int data =10; // Stored in initialized area
    return 0; 
}
[aticleworld@CentOS]$ gcc memory-layout.c -o memory-layout
[aticleworld@CentOS]$ size memory-layout
text       data        bss        dec        hex    filename
960        252          8       1216        4c0    memory-layout

See the size of the data segment has been increased.

 

  • Now add the global uninitialized variable and check the size.
#include <stdio.h> 
 
int data; // Stored in uninitialized area
 
int main(void) 
{ 
    return 0; 
}
[aticleworld@CentOS]$ gcc memory-layout.c -o memory-layout
[aticleworld@CentOS]$ size memory-layout
text       data        bss        dec        hex    filename
960        248          12       1216        4c0    memory-layout

You can see the size of the .bss has been increased.

 

  • Now add the global and static uninitialized variable and check the size.
#include <stdio.h> 
 
int data1; //Stored in uninitialized area
 
int main(void) 
{ 
    static int data2; //Stored in uninitialized area
   
    return 0; 
}
[aticleworld@CentOS]$ gcc memory-layout.c -o memory-layout
[aticleworld@CentOS]$ size memory-layout
text       data        bss        dec        hex    filename
960        248          16       1216        4c0    memory-layout

The size of .bss increases as per the uninitialized global and static variables.

 

  • Now add the global and static initialized variable and check the size.
#include <stdio.h> 
 
int data1 = 0; //Stored in uninitialized area
 
int main(void) 
{ 
    static int data2 = 0; //Stored in uninitialized area
   
    return 0; 
}
[aticleworld@CentOS]$ gcc memory-layout.c -o memory-layout
[aticleworld@CentOS]$ size memory-layout
text       data        bss        dec        hex    filename
960        264          8       1216        4c0    memory-layout

The size of the data segment increases as per the initialized global and static variables.

 

In the data segment, I have said that the “data segment can be further classified into the two-part initialized read-only area and an initialized read-write area”. So let us see two C programs to understand this concept.

#include <stdio.h>

char str[]= "Amlendra Kumar";

int main(void)
{
    printf("%s\n",str);

    str[0]='k';

    printf("%s\n",str);

    return 0;
}

Output:

Amlendra Kumar
kmlendra Kumar

 

You can see the above example str is a global array, so it will go in the data segment. You can also see that I am able to change the value so it has read and write permission.

Now see the other example code,

#include <stdio.h>

char *str= "Amlendra Kumar";

int main(void)
{
    str[0]='k';

    printf("%s\n",str);

    return 0;
}

 

In the above example, we are not able to change the array character because it is a literal string.  A constant string does not only go in the data section but all types of const global data go in that section.

It is not necessarily that const global and constant strings go in the data section. It can be also in the text section of the program (normally the .rodata segment), as it is normally not modifiable by a program.

 

MCQ On Memory layout in C Programs:

3 votes, 5 avg

You have 15 minutes to take the MCQ On Memory Layout

Your time has been Over.


MCQ On Memory Layout of C Programs

1 / 15

Which statement accurately describes the memory layout of a C program?

2 / 15

Which of the following is NOT a valid C string escape sequence?

3 / 15

In a typical memory layout of a C program, where are function instructions stored?

4 / 15

What can cause a stack overflow?

5 / 15

What is the primary purpose of the .bss segment within the Data Segment?

6 / 15

What does the .bss section in the Data Segment represent?

7 / 15

What information is NOT stored in a stack frame during a function call?

8 / 15

Which memory segment's growth direction in C is generally considered to be downward (towards lower memory addresses)?

9 / 15

In which memory segment are constants often stored in a C program?

10 / 15

Which register typically points to the top of the stack in many architectures?

11 / 15

Which function is used to explicitly allocate memory on the stack in C?

12 / 15

Which segment of memory holds the compiled binary code, and is typically marked as read-only?

13 / 15

Which memory segment can exhibit fragmentation over time due to frequent allocation and deallocation of varying-sized memory blocks?

14 / 15

Which compiler optimization might affect the creation of stack frames during function calls?

15 / 15

What happens if a string literal is modified in C?

Your score is

The average score is 83%

0%

 

Recommended Posts for you

 



12 comments

  1. Thanks for giving simple explanation. I couldn’t pick up some points in class. Now that doubts are clear.

    1. Text area depends on the binary size (code size). If the size of binary increases text area also increases. You can check the memory map (.map) file for better understanding.

Comments are closed.