Skip to main content

Section 22.2 Stack Issues

The stack enables programmers to not have to worry about memory management for local variables and function parameters. When you call a function, space is allocated and when it is done, that memory is reclaimed. The programmer doesn’t have to worry about where in memory each variable should go, it is all handled for them, no matter what series of function calls takes place.
However, there are some significant limitations to the stack.

Subsection 22.2.1 Limited Size

One limitation is that the stack has a fixed size, which is determined when the program starts. If a program uses too much stack memory (for example, by making too many recursive function calls), it can cause a stack overflow error. This size is usually measured in megabytes. Which means that if you try to put a very large piece of data on the stack it can cause a stack overflow.
Try this program, which tries to allocate a large block of 1,000,000,000 integers. (Don’t worry about the syntax int data[1000000000];, it will be explained soon.)
Listing 22.2.1.
You should get a β€œsegmentation fault”. Which is a runtime error indicated you went outside a segment of memory (the stack). Each integer takes 4 bytes and \(1,000,000,000 \times 4\) is \(4,000,000,000\) bytes, or approximately 4 GB. This exceeds the space available. Try reducing the number in the code to 1 million (1000000). That should run just fine as \(4,000,000\) bytes is approximately 4 MB, which the stack has room for.

Subsection 22.2.2 Limited Duration

Another limitation of the stack is that the lifespan of all local variables and parameters are tied to the function call. When a function returns, all of its local variables and parameters are destroyed. This means that if you try to return a pointer or reference to a local variable, it will point to memory that is no longer valid. This is referred to as undefined behavior as there is literally no definition for what should happen in this case.
The compiler knows this and will try to prevent you from making this mistake:
Listing 22.2.2.
If you force the compiler to ignore the issue (this next example uses the compiler flag -Wno-return-local-addr to ignore warnings about returning local addresses), something bad will happen. Try it here:
Listing 22.2.3.

Activity 22.2.1.

When you run the code above, main passes 5 to foo, which stores it in x and then makes a pointer that points at x. It returns that address to main.
What is the bad thing that happened?
  • The program crashes with a segmentation fault.
  • Incorrect. The memory address stored in badPointer is not currently valid, but it is in a valid memory segment.
  • The program prints a random looking value (that can change with each run).
  • Correct, the pointer to x is no longer valid in main, so accessing it results in undefined behavior.
  • The program runs without any issues.
  • Incorrect.
  • The program fails to build with a compiler error.
  • Incorrect.
Now lets try running the same code using codelens:

Checkpoint 22.2.1.

Run the code in codelens and answer the question it asks you:
The address stored in badPointer is shown as a poop emoji (πŸ’©) because it holds an address that does not currently point at a valid integer. It has the address that WAS used for x in foo. But there is no telling what that address is currently being used for. Rather than let you use the pointer, Codelens causes an error when line 17 tries to dereference the pointer.
The critical thing in these examples is that there is no safe way for a function to return a memory address for data that was created in its stack frame. All of the memory for the function disappears when it returns.
If we want to write a function that creates some memory that will be used later on in the program, we can not use stack allocation.
You have attempted of activities on this page.