Memory Management and Execution on Windows — C++ (MSVC)

Last updated: December 13, 2025
Author: Paul Namalomba
- SESKA Computational Engineer
- Software Developer
- PhD Candidate (Civil Engineering Spec. Computational and Applied Mechanics)

Contact: kabwenzenamalomba@gmail.com
Website: paulnamalomba.github.io

Language: C++ Compiler: MSVC License: MIT

Overview

This guide explains how C++ (MSVC on Windows) handles key memory and execution concepts: value vs reference semantics, stack vs heap storage, copy semantics (deep vs shallow), and nullability. The content focuses on what actually happens under the hood on Windows using MSVC, with short, runnable examples and Windows-specific notes.

Contents


Windows — Compile & Run (Developer Command Prompt / PowerShell)

Notes: Use the "Developer Command Prompt for VS" (recommended) which sets up MSVC environment variables. If using PowerShell, run the vcvarsall.bat script from your Visual Studio installation first.

cmd REM open "Developer Command Prompt for VS" cl /EHsc main.cpp /Fe:main.exe main.exe

powershell & 'C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat' x64 cl.exe /EHsc main.cpp /Fe:main.exe .\main.exe


Overview

This guide explains how C++ (MSVC on Windows) handles key memory and execution concepts: value vs reference semantics, stack vs heap storage, copy semantics (deep vs shallow), and nullability. The content focuses on what actually happens under the hood on Windows using MSVC, with short, runnable examples and Windows-specific notes.


Contents


1) Data Structures — Value-Type vs Reference-Type

C++ does not have a single runtime-managed split of "value vs reference" like some managed languages. Instead:

Key points: - Declaring an object MyType x; instantiates a value object. If you use MyType* p = new MyType(); you allocate an object on the heap and p stores its address. - struct vs class in C++ differ only by default access (public vs private).

Example:

struct Point { int x; int y; };

void example() {
    Point a{1,2};        // value: object `a` exists directly
    Point b = a;         // copy constructed (value copy)

    Point* ph = new Point{3,4}; // heap allocation
    delete ph;                 // must free
}

Notes/Tips: - Value semantics by default makes copying cheap for small objects but expensive for large ones. - Use pointers/references for polymorphism and dynamic lifetime control.


2) Storage — Stack vs Heap

Where objects live depends on how they are created:

Windows/MSVC specifics: - MSVC uses the OS virtual memory APIs for heap allocation; default CRT heap allows fragmentation handling, low-fragmentation heap options exist for production (LFH). - Stack size is configurable per-thread (default ~1MB for Windows threads created by the runtime). Watch recursion and large stack arrays.

Example:

void foo() {
    int stackArray[1024]; // on the stack
    int* heapArray = new int[1024]; // on the heap
    // ...
    delete[] heapArray; // free heap memory
}

Pitfalls: - Stack overflow if you allocate huge local arrays or have deep recursion. - Memory leaks if delete not called or exceptions bypass cleanup. Use RAII and smart pointers.


3) Copy Semantics — Shallow vs Deep & Move Semantics

C++ copy semantics are controlled by copy constructor and assignment operator. By default the compiler-generated copy is a member-wise copy (shallow copy of fields).

Example (shallow vs deep):

struct Buffer {
    size_t size;
    char* data;

    // default shallow copy (compiler-generated)
    // custom deep copy
    Buffer(const Buffer& other) : size(other.size) {
        data = new char[size];
        std::memcpy(data, other.data, size);
    }

    // move constructor (steal ownership)
    Buffer(Buffer&& other) noexcept : size(other.size), data(other.data) {
        other.data = nullptr; other.size = 0;
    }

    ~Buffer() { delete[] data; }
};

Best practices: - Prefer RAII containers (std::vector, std::string) which implement deep/move semantics correctly. - Implement move operations for expensive-to-copy resources. - Rule of five / zero: if you implement destructor/copy/move, follow the rule of five (or prefer no manual resource management and rely on standard containers).


4) Nullability

Example:

std::unique_ptr<Foo> p = std::make_unique<Foo>();
if (p) p->Do(); // check for non-null

std::optional<int> maybe;
maybe = 42;
if (maybe.has_value()) { int v = *maybe; }

Pitfalls: - Do not mix delete and delete[] incorrectly. - Avoid raw owning pointers; prefer unique_ptr/shared_ptr. - Beware of shared_ptr cycles causing leaks — use weak_ptr to break cycles.


Examples — Small Runnable Snippets

main.cpp:

#include <iostream>
#include <memory>
#include <optional>
#include <vector>

struct Node { int val; std::unique_ptr<Node> next; };

int main() {
    // Stack vs Heap
    int stackValue = 10;                 // stack
    auto heapValue = std::make_unique<int>(20); // heap via unique_ptr
    std::cout << "stack: " << stackValue << " heap: " << *heapValue << "\n";

    // Deep vs shallow through vectors
    std::vector<int> a = {1,2,3};
    auto b = a; // deep copy (vector owns its memory) but shallow for elements that are pointers

    // Move
    auto c = std::move(a); // a is now empty, c has data without copying

    // Optional
    std::optional<int> maybe;
    maybe = 100;
    if (maybe) std::cout << "maybe: " << *maybe << "\n";

    return 0;
}

Compile & run with MSVC as described earlier.


Windows / MSVC Specific Notes and Pitfalls


References


End of C++ guide.