Skip to Content
Course content

53: Stack vs Heap Memory

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Wait, if the stack is automatic, why do I even need a heap?

Think of the stack as your immediate workspace—the stuff you need right now for the function you're currently in. It's incredibly fast because the CPU has a dedicated pointer just to keep track of where the "top" of the stack is. When a function finishes, the stack pointer just slides back down, and everything in that function's frame is effectively gone. It's clean, it's efficient, and you don't have to think about it.

But the stack has a major limitation: size and lifetime. You have to know exactly how much memory you need at compile time (or at least before the function starts), and that memory vanishes the second the function returns. If you're writing a program to load a text file, you have no idea if that file is 10 lines or 10 million lines. You can't just declare char buffer[10000000] on the stack because you'll risk a stack overflow, which crashes your program instantly.

That's where the heap comes in. The heap is a giant pool of memory available to your whole program. You ask for a chunk of it using malloc(), and the OS finds a spot for you. It stays there until you explicitly say, "I'm done with this," using free(). It gives you flexibility and longevity that the stack simply can't provide.

Is the heap actually slower?

Yes, it is. I've seen people try to optimize their code by putting everything on the heap, thinking "dynamic" means "better," but it's the opposite in terms of raw speed.

When you allocate on the stack, the CPU just moves a pointer. When you call malloc(), the C runtime has to search the heap for a block of memory large enough to fit your request. It has to keep track of which blocks are used and which are free. Then, to actually use that memory, you're dealing with pointers—meaning the CPU has to do an extra "hop" to find the actual data in RAM. It's a small difference for one variable, but if you're doing it millions of times in a loop, you'll feel it.

How do I know which one I'm using in my code?

It's actually pretty straightforward once you look for the "trigger" keywords. If you declare a variable normally inside a function, it's on the stack. If you use malloc, calloc, or realloc, you're dealing with the heap.

void example() {
    int age = 25;                // Stack: fast, automatic, disappears at the end of the function.
    
    int *scores = malloc(5 * sizeof(int)); // Heap: slower, manual, stays until you call free().
    
    // ... do work ...
    
    free(scores);                // If I forget this, I've got a memory leak.
}

One critical rule I want you to remember: Never return a pointer to a stack variable from a function. If you create an array locally in a function and return its address, you're returning a pointer to memory that the program now considers "free." The next function that gets called will overwrite that space, and your data will turn into complete gibberish.

What happens if I just... don't call free()?

In a tiny program that runs for half a second and then exits, the OS will usually clean up after you. But in a real-world application—like a game engine or a web server that runs for weeks—forgetting to free() is a death sentence. This is a memory leak.

Imagine your program allocates 1KB of memory every time a user clicks a button, but never releases it. Eventually, your program will eat up every byte of available RAM on the system. The OS will either start swapping memory to the hard drive (making everything crawl) or the OOM (Out Of Memory) killer will simply terminate your process. I've spent hours debugging "random" crashes only to find a missing free() inside a deeply nested loop.




📋 Practical Task

Exercise: The Dynamic User List Manager

You need to build a small utility that simulates a user list. Because you don't know how many users will join, you cannot use a fixed-size array on the stack.

  • Write a program that asks the user how many usernames they want to enter.
  • Use malloc to allocate exactly enough memory to hold that number of strings (each string can be a fixed size, e.g., 50 characters, but the array of strings must be dynamic).
  • Fill the list with names provided by the user via scanf.
  • Print the full list back to the console.
  • Crucially: Implement the cleanup phase. You must free the memory to ensure no leaks occur.

Goal: Verify your program doesn't crash and that you've correctly transitioned from a user-defined size to a heap allocation.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.