Memory Layout in C

Memory layout in C is the way a program’s memory is organized while it runs. This is one of the most important topics for understanding how C programs actually work at runtime. Many beginners learn variables, functions, arrays, and pointers separately, but memory layout connects all of them. It explains where code is stored, where global variables live, where local variables are created, and how dynamic memory is managed.

Since C gives direct access to memory-related concepts, understanding the memory layout helps in debugging, avoiding crashes, writing better embedded software, and understanding stack overflow, memory leaks, and pointer errors. In this article, we will understand memory layout in C, the main memory sections, what kind of data goes into each section, and the common mistakes that happen when programmers ignore this model.

What is Memory Layout in C?

Memory layout in C is the organization of different parts of a running program in memory. A compiled C program is not placed in memory as one flat block. Instead, the operating system and runtime divide it into logical sections such as code, initialized data, uninitialized data, heap, and stack.

Each section has a different purpose and stores different kinds of program information.

Memory layout in C tells us where code, variables, dynamic memory, and function call data are stored while the program runs.

Main Sections in Memory Layout of C Program

  • Text section
  • Initialized data section
  • Uninitialized data section (BSS)
  • Heap
  • Stack

Text Section in C

The text section, also called the code section, stores the compiled machine instructions of the program. In simple terms, this is where the actual executable code of functions is placed.

  • contains program instructions
  • usually read-only
  • may also include read-only constants on some systems

The code of main() and other functions belongs here.

Initialized Data Section in C

This section stores global and static variables that have been given an initial value.

Example:

int global_value = 10;
static int count = 5;

Since these variables already have values before execution begins, they are placed in the initialized data section.

Uninitialized Data Section (BSS) in C

The BSS section stores global and static variables that are declared but not explicitly initialized.

Example:

int global_number;
static int total;

These variables are automatically initialized to zero by default, but they are still treated separately from initialized data.

Heap in C

The heap is the memory region used for dynamic memory allocation. When memory is requested at runtime using functions like malloc(), calloc(), or realloc(), that memory usually comes from the heap.

Example:

#include <stdlib.h>

int *ptr = (int *)malloc(sizeof(int) * 5);

The heap is controlled manually by the programmer in C. If allocated memory is not released with free(), memory leaks can occur.

Stack in C

The stack stores local variables, function parameters, and function call information. Every time a function is called, a new stack frame is created. When the function returns, that frame is removed.

Example:

void test(void)
{
    int local_var = 20;
}

Here local_var is usually stored on the stack.

Deep recursion or very large local arrays can cause stack overflow if too much stack memory is consumed.

Where Different Variables are Stored in C

Variable TypeCommon Memory Section
Function codeText section
Global initialized variableInitialized data section
Global uninitialized variableBSS section
Static initialized variableInitialized data section
Static uninitialized variableBSS section
Local variableStack
Dynamically allocated memoryHeap

Example Program for Memory Layout in C

The following program helps visualize where different variables come from.

#include <stdio.h>
#include <stdlib.h>

int global_init = 10;
int global_uninit;
static int static_init = 20;
static int static_uninit;

int main(void)
{
    int local_var = 30;
    int *heap_var = (int *)malloc(sizeof(int));
    *heap_var = 40;

    printf("Address of global_init = %p\n", (void *)&global_init);
    printf("Address of global_uninit = %p\n", (void *)&global_uninit);
    printf("Address of static_init = %p\n", (void *)&static_init);
    printf("Address of static_uninit = %p\n", (void *)&static_uninit);
    printf("Address of local_var = %p\n", (void *)&local_var);
    printf("Address of heap_var data = %p\n", (void *)heap_var);

    free(heap_var);
    return 0;
}

The exact addresses vary between systems, but the pattern shows that different kinds of data come from different regions.

String Literals and Read-Only Memory in C

String literals such as "Hello" are typically stored in a read-only area related to the program image. Modifying them directly is not safe.

char *msg = "Hello";

In contrast, a character array like char msg[] = "Hello"; creates writable storage for the characters.

Stack vs Heap in C

PointStackHeap
AllocationAutomaticManual
LifetimeUntil function returnsUntil freed explicitly
SpeedUsually fasterUsually slower
Use caseLocal variables and call framesDynamic runtime memory
Common issueStack overflowMemory leak

Understanding this difference is critical for working with pointers and dynamic memory safely.

Why Memory Layout in C Matters

  • It helps explain pointer behavior.
  • It helps in debugging crashes and invalid access.
  • It explains why local variables disappear after function return.
  • It helps understand static storage duration.
  • It is essential for embedded systems and low-level programming.

Common Mistakes Related to Memory Layout in C

  • returning the address of a local variable
  • forgetting to free heap memory
  • confusing static and local storage duration
  • trying to modify string literals directly
  • assuming all variables are stored in the same place
MistakeProblemBetter Practice
Returning local variable addressPointer becomes invalid after function returnsUse dynamic memory or caller-owned storage
Never calling free()Heap memory leakRelease allocated memory after use
Modifying string literalUndefined behaviorUse a writable character array if modification is needed

Best Practices for Understanding Memory Layout in C

  • Know the difference between stack, heap, data, and BSS.
  • Use static only when the required lifetime is longer than a function call.
  • Free heap memory after use.
  • Avoid returning pointers to local variables.
  • Use small experiments with addresses and sizeof to build intuition.

FAQs

What is memory layout in C?

Memory layout in C is the organization of a running program into sections such as text, data, BSS, heap, and stack.

Where are local variables stored in C?

Local variables are commonly stored on the stack.

Where are global variables stored in C?

Initialized global variables usually go to the data section, while uninitialized globals usually go to the BSS section.

What is the difference between stack and heap in C?

The stack is used for automatic local storage and function calls, while the heap is used for dynamic memory allocation.

Why is memory layout important in C?

It helps explain variable lifetime, pointer safety, dynamic memory behavior, and many common runtime bugs.

Can string literals be modified in C?

They should not be modified directly. If writable text is needed, use a character array instead.