Skip to content

Understanding Stack Overflow in C

This article is intended for intermediate and advanced C programmers. It explains what a stack overflow is, why it occurs, the common programming mistakes that cause it, and practical techniques for preventing stack-related failures in C applications.


1. What Is a Stack Overflow?

Every time a function is called, the operating system allocates a small block of memory on the Stack to hold information needed for that function call. This block is known as a stack frame.

A stack frame typically contains:

  • Local variables
  • Function parameters
  • Return address
  • Saved CPU registers

As functions are called, new stack frames are pushed onto the Stack.

Function Calls

main()
   |
   +--> process()
            |
            +--> calculate()
                     |
                     +--> display()

Resulting Stack:

Higher Memory Addresses

+----------------------+
| display()            |
+----------------------+
| calculate()          |
+----------------------+
| process()            |
+----------------------+
| main()               |
+----------------------+

Lower Memory Addresses

When a function returns, its stack frame is removed, allowing the memory to be reused by subsequent function calls.


The Stack, however, has a fixed size.

Unlike the Heap, which can often grow as needed (subject to available memory), the Stack is allocated a limited amount of memory when a program or thread starts.

For example, a program might be given an 8 MB stack.

Program Stack (8 MB)

+----------------------+
|                      |
|   Available Stack    |
|                      |
+----------------------+

Every function call consumes part of this space.

If the program keeps creating stack frames until no space remains, the next function call cannot be placed on the Stack.

Before Overflow

+----------------------+
| Function D           |
+----------------------+
| Function C           |
+----------------------+
| Function B           |
+----------------------+
| Function A           |
+----------------------+
| Free Stack Space     |
+----------------------+

        ↓ Another function call

+----------------------+
| Function E           |  ← No room available
+----------------------+

At this point, the program has experienced a stack overflow.

Most operating systems immediately terminate the program, often producing an error such as:

  • Segmentation fault
  • Stack overflow exception
  • Access violation

depending on the operating system and compiler.

A stack overflow is therefore not caused by using the Stack itself, but by attempting to use more stack memory than has been allocated.

The two most common causes are:

  1. Creating very large local variables.
  2. Excessive or infinite recursion.

The following section examines each of these causes in detail.


2. Common Causes of Stack Overflow

A stack overflow occurs when the total amount of memory required by stack frames exceeds the size of the Stack.

Although many situations can contribute to this problem, two programming mistakes account for the vast majority of stack overflows:

  1. Creating very large local variables.
  2. Excessive or infinite recursion.

2.1 Large Local Variables

Every local variable declared inside a function is stored on the Stack.

For example,

void process(void)
{
    int values[100];
}

The array values is allocated as part of the function's stack frame.

Stack

+---------------------------+
| Return Address            |
+---------------------------+
| Local Variables           |
| values[100]               |
+---------------------------+

Since this array is relatively small, it is unlikely to cause any problems.

Now consider a much larger array.

void process(void)
{
    int values[1000000];
}

Assuming an int occupies 4 bytes, the array requires approximately:

1,000,000 × 4 = 4,000,000 bytes
               ≈ 4 MB

If the program has an 8 MB stack, a single function call immediately consumes about half of the available stack space.

8 MB Stack

+---------------------------+
| Remaining Stack (~4 MB)   |
+---------------------------+
| values[1000000] (4 MB)    |
+---------------------------+

Calling additional functions or declaring more local variables may quickly exhaust the remaining stack space, resulting in a stack overflow.

For large data structures, allocating memory on the Heap is usually a better choice.

void process(void)
{
    int *values = malloc(1000000 * sizeof(int));

    if (values != NULL)
    {
        /* Use the array */

        free(values);
    }
}

Here, only the pointer values occupies stack space, while the large array itself is allocated on the Heap.


2.2 Excessive or Infinite Recursion

Every function call creates a new stack frame.

When a function calls itself recursively, each recursive call adds another frame to the Stack.

void count(int n)
{
    printf("%d\n", n);

    count(n + 1);
}

This function has no stopping condition.

The sequence of function calls looks like this.

count(0)
   |
   +--> count(1)
           |
           +--> count(2)
                   |
                   +--> count(3)
                           |
                           +--> ...

Each call creates another stack frame.

Higher Memory Addresses

+----------------------+
| count(1000)          |
+----------------------+
| count(999)           |
+----------------------+
| count(998)           |
+----------------------+
|         ...          |
+----------------------+
| count(1)             |
+----------------------+
| count(0)             |
+----------------------+

Lower Memory Addresses

Eventually, the Stack becomes full, and the next recursive call cannot be accommodated, causing a stack overflow.

Even when recursion has a stopping condition, very deep recursion can still exhaust the Stack.

void countdown(int n)
{
    if (n == 0)
        return;

    countdown(n - 1);
}

If n is sufficiently large, thousands or even millions of recursive calls may be created before the function begins returning, consuming all available stack space.

Whenever recursion is used, ensure that:

  • a valid termination condition exists,
  • each recursive call moves toward that condition, and
  • the maximum recursion depth remains within the available stack size.

The next section discusses practical techniques for preventing stack overflows in C programs.


3. Preventing Stack Overflow

Although stack overflows can be difficult to debug, they are usually easy to prevent by following a few good programming practices.


3.1 Avoid Large Local Variables

Large arrays and structures should generally not be allocated as local variables.

For example, instead of writing:

void process(void)
{
    char buffer[10 * 1024 * 1024];
}

consider allocating the memory on the Heap.

void process(void)
{
    char *buffer = malloc(10 * 1024 * 1024);

    if (buffer != NULL)
    {
        /* Use the buffer */

        free(buffer);
    }
}

In this example:

  • the pointer buffer occupies only a few bytes on the Stack,
  • the large memory block is allocated on the Heap.

As a general guideline, local variables should remain reasonably small.


3.2 Be Careful with Recursion

Recursion is a powerful programming technique, but every recursive call consumes additional stack space.

Always ensure that:

  • the recursion has a termination condition,
  • every recursive call moves toward that condition,
  • the recursion depth is reasonably bounded.

For example,

int factorial(int n)
{
    if (n <= 1)
        return 1;

    return n * factorial(n - 1);
}

This recursion is safe because each call reduces n, eventually reaching the base case.

In contrast,

void func(void)
{
    func();
}

never reaches a stopping condition and will eventually cause a stack overflow.

For algorithms that may require thousands of recursive calls, an iterative solution is often a better choice.


3.3 Know Your Stack Size

The available stack space depends on the operating system, compiler, and target hardware.

For example:

  • Desktop operating systems often provide several megabytes of stack space.
  • Embedded systems may provide only a few kilobytes.

The following illustration shows why code that works correctly on one system may fail on another.

Desktop Application

Stack Size
+----------------------+
|        8 MB          |
+----------------------+

Embedded System

Stack Size
+----------------------+
|        8 KB          |
+----------------------+

A function that allocates a 20 KB local array may execute without problems on a desktop computer but immediately overflow the stack on an embedded device.

When writing portable software, avoid assuming that large amounts of stack memory are available.


3.4 Use Static or Global Storage When Appropriate

Some data structures need to exist for the entire lifetime of the program. See Memory Segmentation for more details.

In such cases, storing them as static or global variables avoids consuming stack space on every function call.

Instead of:

void process(void)
{
    char lookupTable[4096];
}

consider:

static char lookupTable[4096];

void process(void)
{
    /* Use lookupTable */
}

The array is now stored in the Data or BSS segment rather than on the Stack.

This approach should be used only when the data genuinely has program-wide or persistent lifetime, not simply to avoid using the Stack.


By keeping local variables reasonably small, limiting recursion, understanding the available stack size, and choosing the appropriate memory segment for large data structures, stack overflows can usually be avoided before they become difficult runtime bugs.

The next section summarizes the key concepts discussed in this article.


4. Summary

A stack overflow occurs when a program attempts to use more stack memory than has been allocated. Since every function call creates a new stack frame, excessive stack usage eventually exhausts the available space, causing the program to terminate or crash.

The two most common causes of stack overflow are:

  • Declaring very large local variables.
  • Excessive or infinite recursion.

Large local arrays can consume significant amounts of stack memory with a single function call, while recursive functions continuously create new stack frames until no stack space remains.

To reduce the risk of stack overflow:

  • Keep local variables reasonably small.
  • Allocate large data structures on the Heap when appropriate.
  • Ensure recursive functions always have a valid termination condition.
  • Avoid unnecessarily deep recursion.
  • Be aware of the stack size available on your target platform, especially in embedded systems where stack memory is often very limited.

Understanding how the Stack is used during function calls allows you to write programs that are both more reliable and easier to debug. By managing stack usage carefully, many runtime failures can be prevented long before the program is deployed.


C/C++ Material on this website

(such as memory managment, pointers, embedded C programming etc.)