Memory Management: Stack vs Heap in C/C++

Program Memory Segmentation

A compiled C/C++ program utilizes memory divided into several distinct segments:

  • Stack segment - Automatically managed by the compiler, storing function parameters and local variables. Operates as a LIFO structure.
  • Heap segment - Manually allocated and freed by programmers. Unreleased memory may be reclaimed by the operating system upon program termination.
  • Global/Static segment - Contains global and static variables, with initialized and uninitialized variables in adjacent regions.
  • Constant segment - Stores constant strings and read-only data.
  • Code segment - Contains the executable binary code of functions.

Memory Allocation Example

// memory_demo.c
int global_x = 0;     // Global initialized section
int global_y;         // Global uninitialized section
char *global_ptr;     // Global uninitialized section

int main() {
    int local_a;              // Stack
    char local_arr[] = "test";// Stack
    char *local_ptr;          // Stack
    char *const_ptr = "data"; // String in constant section, pointer on stack
    static int static_z = 0;  // Global static section
    
    global_ptr = (char*)malloc(15);
    local_ptr = (char*)malloc(25);
    // Allocated 15 and 25 byte regions reside in heap
    strcpy(global_ptr, "information");
    return 0;
}

Stack and Heap Characteristics

Allocation Methods

Stack: System automatically allocates memory. For example, declaring int value; inside a function automatically reserves stack space.

Heap: Programmers manually allocate using malloc() in C or new in C++. Note that the pointer variables themselves reside on the stack.

System Response to Allocation

Stack: System provides memory if sufficient stack space remains, otherwise reports stack overflow.

Heap: OS maintanis a free memory address list. Upon allocation request, it finds adequate space, removes it from the free list, and records allocation size for proper deallocation.

Size Limitations

Stack: Grows toward lower addresses as a contiguous memory block with predetermined capacity (typically 1-2MB in Windows).

Heap: Expands toward higher addresses as non-contiguous memory limited by available virtual memory.

Performance Comparison

Stack: Fast automatic allocation but limited programmer control.

Heap: Slower allocation with potential fragmentation but offers greater flexibility.

Storage Content

Stack: Stores function return addresses, parameters (typically right-to-left), and local variables during function calls.

Heap: Contains programmer-defined data structures with allocation size metadata.

Access Efficiency

#include <stdio.h>

void demo() {
    char result;
    char array_data[] = "9876543210";
    char *pointer_data = "9876543210";
    
    result = array_data[2];  // Direct array access
    result = pointer_data[2]; // Indirect pointer access
}

Array access typically outperforms pointer access due to direct memory addressing versus indirection through pointer values.

Windows Process Memory Architecture

Program variables distribute across different memory regions based on their type:

#include <stdio.h>

int global1 = 0, global2 = 0, global3 = 0;

int main() {
    static int static1 = 0, static2 = 0, static3 = 0;
    int local1 = 0, local2 = 0, local3 = 0;
    
    // Display memory addresses
    printf("Local: 0x%08x, 0x%08x, 0x%08x\n", &local1, &local2, &local3);
    printf("Global: 0x%08x, 0x%08x, 0x%08x\n", &global1, &global2, &global3);
    printf("Static: 0x%08x, 0x%08x, 0x%08x\n", &static1, &static2, &static3);
    return 0;
}

Output demonstrates contiguous allocation within segments but significant address gaps between local and global/static variables.

High Memory Address ├─────────────┤ │ Static Data │ ├─────────────┤ │ Code │ ├─────────────┤ │ Dynamic Data│ ├─────────────┤ │ ... │ Low Memory Address


</div>### Functon Call Mechanics

#include <stdio.h>

void __stdcall example(int x, int y, int z) { int a = x; int b = y; int c = z;

printf("Parameters: 0x%08x, 0x%08x, 0x%08x\n", x, y, z);
printf("Locals: 0x%08x, 0x%08x, 0x%08x\n", &a, &b, &c);

}

int main() { example(10, 20, 30); return 0; }


Stack frame organization during function execution:

<div style="text-align: center;">```

Stack Top (Low Memory)
├─────────────┤
│ Local Var C │
├─────────────┤
│ Local Var B │
├─────────────┤
│ Local Var A │
├─────────────┤
│ Return Addr │
├─────────────┤
│ Parameter 1 │
├─────────────┤
│ Parameter 2 │
├─────────────┤
│ Parameter 3 │
├─────────────┤
Stack Base (High Memory)
#include <stdio.h>
#include <string.h>

void __stdcall vulnerable() {
    char small_buffer[8] = "\0";
    strcat(small_buffer, "EXCESSIVEDATA");
    return;
}

int main() {
    vulnerable();
    return 0;
}

This demonstrates how exceeding buffer boundaries can overwrite critical stack data, including return addresses.

Heap-Based Memory Allocation

#include <stdio.h>
#include <windows.h>

void allocation_demo() {
    char *heap_buffer = new char[128];
    char stack_buffer[128];
    static char static_buffer[128];
    
    printf("Heap: 0x%08x\n", heap_buffer);
    printf("Stack: 0x%08x\n", stack_buffer);
    printf("Static: 0x%08x\n", static_buffer);
}

int main() {
    allocation_demo();
    return 0;
}

Windows Heap API Functions

  • HeapAlloc - Allocate heap memory
  • HeapCreate - Create new heap object
  • HeapDestroy - Remove heap object
  • HeapFree - Release allocated memory
  • GetProcessHeap - Retrieve default process heap

Heap Allocation Example

#include <windows.h>

int main() {
    HANDLE process_heap = GetProcessHeap();
    char *block1 = (char*)HeapAlloc(process_heap, 0, 16);
    char *block2 = (char*)HeapAlloc(process_heap, 0, 16);
    
    printf("Heap: 0x%08x\n", process_heap);
    printf("Block1: 0x%08x\n", block1);
    printf("Block2: 0x%08x\n", block2);
    
    HeapFree(process_heap, 0, block1);
    HeapFree(process_heap, 0, block2);
    return 0;
}

Memory Data Alignment

Data alignment ensures variables reside at memory addresses divisible by their size, optimizing CPU access performance.

#include <stdio.h>

int main() {
    int integer_var;
    char character_var;
    int another_int;
    
    printf("Integer1: 0x%08x\n", &integer_var);
    printf("Character: 0x%08x\n", &character_var);
    printf("Integer2: 0x%08x\n", &another_int);
    return 0;
}

Compiler output varies, but all maintain proper alignment, sometimes padding smaller types to alignment boundaries.

Tags: memory-management stack heap c-programming Windows-API

Posted on Mon, 18 May 2026 05:47:20 +0000 by y4m4