Every variable a running program creates lives in one of two regions of memory: the stack or the heap. Which one it ends up in isn't arbitrary — it depends on the variable's size, its lifetime, and, in a language like C++, an explicit choice the programmer makes.

The stack is fast and structured: memory is allocated and freed in strict last-in-first-out order, tied directly to function calls. When a function is called, its local variables get pushed onto the stack; when the function returns, all of that memory is freed automatically, instantly, just by moving a pointer back. That's why stack allocation is essentially free compared to the heap, and why local variables of a known, fixed size default to living there.

The heap has no such structure. It's a large pool of memory that can be allocated and freed in any order, which makes it flexible — necessary for anything whose size isn't known until runtime, or whose lifetime needs to outlive the function that created it — but that flexibility has a real cost: the runtime has to actually track which parts of the heap are in use, and something has to eventually free each allocation.

C++ makes heap management the programmer's explicit responsibility at the language level: new allocates on the heap, and delete has to be called on that exact same pointer later, or the memory leaks — allocated but never freed, unreachable and unusable for the rest of the program's life. Modern C++ mitigates this with RAII (Resource Acquisition Is Initialization) and smart pointers like std::unique_ptr and std::shared_ptr, which tie a heap allocation's lifetime to a stack-allocated object's scope, so the deletion happens automatically when that object goes out of scope — but the underlying stack/heap split is still there under the abstraction.

C# takes a different approach entirely: value types (int, struct, and similar) live on the stack by default, the same as in C++, but reference types (class instances) are always heap-allocated, and the .NET garbage collector — not the programmer — tracks when a heap object is no longer reachable and frees it automatically. That's a real trade: less manual bookkeeping, at the cost of not controlling exactly when a given object gets freed.

The practical upshot in both languages is the same underlying fact expressed two different ways: stack memory is fast, automatic, and scoped tightly to function calls; heap memory is flexible but has to be managed, whether that management is explicit (raw C++ new/delete), semi-automatic (C++ smart pointers), or fully automatic (C#'s garbage collector).