Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsIn C, the useful distinction is not “stack versus heap” but automatic storage duration versus allocated storage duration. Automatic objects end their lifetime when the block that declares them exits. Allocated objects last until your program deallocates or reallocates them. “Stack” and “heap” are the names most implementations use for these two behaviors, but the C language does not require either term to describe physical memory.
Why the terminology matters
C defines four storage durations: automatic, static, thread, and allocated. The standard’s rules are written in terms of when an object’s lifetime begins and ends, and who is responsible for ending it. Nothing in those rules says where the bytes live. A typical implementation places automatic objects in a call stack and allocated objects in a heap, which is why the two names stuck, but a compiler or platform may choose other layouts as long as the observable behavior matches the standard. The distinction matters because a bug that depends on the implementation model (for example, an address that happens to still hold a value) is still a bug under the language rules.
As an Amazon Associate I earn from qualifying purchases.
The rest of this article uses the standard’s terms, with “stack” and “heap” shown as common shorthand.
Automatic storage: the “stack” case
Non-static objects declared inside a block, and function parameters, generally have automatic storage duration. Storage is set up when the declaring block is entered and released when that block exits. Each recursive call gets its own separate set of these objects, so the recursion depth determines how many distinct copies exist at once.
#1 Best Overall
Variable-length arrays follow a slightly different rule: their storage is allocated when the declaration is executed and released when the declaration goes out of scope. Automatic objects are the default choice for values whose useful life matches a function call or a block. You do not call any function to create or destroy them, and you cannot free them early. The storage-duration reference describes these rules in detail.
The dangling-pointer trap
Automatic lifetime has one consequence that catches many learners. A pointer to an automatic object becomes invalid when the object’s block ends, and the pointer does not extend the object’s life. Consider this function:
Rank #2
int *make_value(void)
{
int local = 42;
return &local; /* local's lifetime ends when the function returns */
}
int main(void)
{
int *p = make_value();
printf("%dn", *p); /* undefined behavior: p refers to an object whose lifetime has ended */
return 0;
}
The program may print 42 on one compiler and crash or print garbage on another, and either outcome is consistent with the standard. The lifetime reference uses this same kind of example to show dereferencing after the object’s lifetime has ended. Returning the value of local would be fine; returning its address is the error.
Recommended Free Tools
Allocated storage: the “heap” case
Allocated storage is requested at run time through the allocation functions in the standard library. The main ones are malloc, calloc, and realloc. Storage is released with free, or resized with realloc. The object’s lifetime begins when the allocation function returns and ends when it is reallocated or deallocated. Because the lifetime is controlled by the program rather than by a block, the memory can outlive the function that created it.
Rank #3
That freedom comes with explicit responsibility. The program must keep track of every pointer that refers to the allocation, check whether the request succeeded, and release the storage exactly when it is no longer needed.
What malloc returns
According to the malloc reference, malloc allocates uninitialized storage. On success it returns a pointer suitably aligned for any object type with fundamental alignment. On failure it returns a null pointer. A correct allocation therefore has three steps: request the memory, check the return value, and only then use it.
int *make_value(void)
{
int *p = malloc(sizeof *p);
if (p == NULL) {
return NULL; /* allocation failed; the caller must handle this */
}
*p = 42; /* the storage is uninitialized until assigned */
return p; /* ownership passes to the caller */
}
Two details deserve emphasis. First, malloc does not zero the memory, so reading it before writing is an error. Second, the returned pointer is the only route to the storage. If you overwrite it or let it go out of scope without calling free, the memory is leaked for the rest of the program’s run.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Static and thread storage
Not every object is either automatic or allocated. File-scope objects and objects declared with static have static storage duration and last for the entire program execution. Objects declared _Thread_local have thread storage duration and last for the life of their thread. Both categories are described in the storage-duration reference. Treating C memory as a simple two-way choice leaves out a large part of how real programs store global state.
Best Value
Comparing the two durations
| Question | Automatic storage (“stack”) | Allocated storage (“heap”) |
|---|---|---|
| When does the lifetime start? | When the declaring block is entered | When the allocation function returns |
| When does the lifetime end? | When the block exits (or the declaration goes out of scope for variable-length arrays) | When the object is deallocated or reallocated |
| Who ends the lifetime? | The language, automatically | The program, by calling free or realloc |
| Can it outlive the function that created it? | No; a pointer to it becomes invalid | Yes, until it is released |
| Size known at compile time? | Required for ordinary objects; variable-length arrays are the exception | Not required; the size is chosen at run time |
| Initialization | Set by the program; not stated as zeroed by the storage rules | Uninitialized with malloc |
| Failure when obtaining storage | Not a request-time failure in the same sense; the program does not ask for it | Returns a null pointer on failure, which must be checked |
| Relative speed | Not stated by the standard; no portable figure exists | Not stated by the standard; no portable figure exists |
| Capacity limit | Not stated by the standard; depends on the implementation and platform | Not stated by the standard; depends on the implementation and platform |
The speed and capacity rows are intentionally blank of numbers. The C standard does not establish either, and any specific figure would depend on a particular compiler, operating system, and configuration. If you need such numbers, measure them on the target system.
Choosing between them
- Use automatic storage when the object is only needed while a function runs and its size is fixed at compile time. It is the simplest option, and the compiler handles cleanup.
- Use allocated storage when the object must outlive the block that creates it, or when its size is known only at run time, such as a buffer read from a file.
- Use static storage for values that should persist across calls to a function or across the whole program, and avoid sharing them across threads without synchronization.
- Whichever you choose, make ownership explicit: name the function responsible for each allocation and document who frees it.
Common misconceptions to correct
- “The C standard says local variables live on the stack.” The standard says they have automatic storage duration. The call stack is a common implementation model, not a requirement.
- “A pointer is the heap object.” A pointer is an object in its own right. A local pointer variable has automatic duration even when it holds the address of allocated storage.
- “Heap memory disappears when the function returns.” Allocated storage persists until it is deallocated. What ends when the function returns is the lifetime of the local pointer variable, and the allocation remains unless the program frees it.
- “Heap is always slower” or “the stack has a fixed size.” Neither claim is established by the language standard. Speed and limits depend on the implementation.
- “malloc zeroes memory.” It returns uninitialized storage. Initialize the values yourself before reading them.
Further reading
For a broader introduction to the C language that covers these topics in context, Pearson’s listing for The C Programming Language, Second Edition, by Brian W. Kernighan and Dennis M. Ritchie (paperback, ISBN 9780131103627) describes the book as an introduction to C. It is a general language book rather than a guide to memory management, so treat it as supplementary reading. Edition and availability details may change.
The Bottom Line
Think in terms of lifetimes, not locations. Automatic objects end when their block exits, so never return a pointer to one. Allocated objects last until you free or reallocate them, so check every malloc result and release each allocation exactly once. Speed and capacity comparisons depend on the implementation and should be measured on your own target.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




