How the Compiler Manages Variable Storage | C++ Programming

UPI Study › Courses › C++ Programming › Free lessons › Lesson 8.4 Free with a free account

How the Compiler Manages Variable Storage

C++ Programming · Chapter 8: FunctionsSection 8.4 · lesson 54 of 84video coming soon

← Lifetime and ReachNext lesson: How Arguments Travel →

Earn 3 college credits for C++ ProgrammingRecommended for credit by NCCRS · Computer Science 242 · self-paced · one payment, lifetime access

In this lesson

Behind the scenes of a function call.

  • Tell a variable's lifetime from its visibility
  • Predict automatic and static storage duration
  • Describe a stack frame, on call and on return
  • Explain stack overflow; tell const from constexpr

Transcript

Everything said in the video, section by section (1,487 words). Read along, or search it.

Opening Question

How Locals Appear and Vanish

A local variable appears when a function is called, and is gone when it returns. You've been relying on that for some time, without asking how it happens. The answer explains several things at once. Why a local starts fresh on every call, why some variables remember, why returning a reference to a local is a serious fault. And why a program can run out of room by calling itself too many times.

Key Distinction

Scope Versus Storage Duration

Two questions are easy to run together, and keeping them apart makes the rest of this lesson straightforward. Where can this name be used? That's scope, answered by where the declaration sits in the source. When does this storage exist? That's storage duration, answered by what kind of storage the compiler gave it. They usually move together, which is why they get confused. A local is visible inside its block, and exists for roughly as long. But they can separate. A static local is visible only inside one function, and yet exists for the whole run of the program. Once you've seen that case, the two ideas stay apart for good.

Worked Example

Automatic Locals Versus Static Locals

Parameters and ordinary local variables have automatic storage duration. It's the default, and needs no keyword. The storage is created when execution reaches the declaration, and released when the enclosing block ends. Nothing survives. So this counter doesn't count: three calls print 1, 1, 1. There's no single counter. There are three, one per call, each created, incremented, and destroyed. That's what you want almost all the time. Now writing static in front of the declaration changes its storage duration entirely. It's created once, before it's first used, and lives until the program ends. Its initialization runs only the first time. Three calls print 1, 2, 3. In both versions, the name is visible only inside the function. Only the lifetime differs.

Side Note

The Many Meanings of Static and Auto

A side note. The keyword auto once meant exactly this, automatic storage, and was so redundant nobody wrote it. Modern C++ reuses it for something entirely different: asking the compiler to work out a variable's type. Variables declared outside every function also have static storage duration. They exist from before main begins until after it ends. So static describes a lifetime, and several quite different declarations share it.

Comparison

Automatic and Static Storage Side by Side

Compare the two directly. Automatic storage is an ordinary local or parameter. It's created each time the declaration is reached, released when the block ends, and initialized every time. Static storage is created once, before first use, released when the program ends, and initialized only once. Later calls skip it. An uninitialized automatic variable holds whatever was in that memory, and reading it is undefined behavior. An uninitialized static one is set to zero. Automatic variables live in the function's stack frame. Static ones live in a fixed area set aside for the whole run. Use static locals sparingly. A static local makes a function remember, so the same arguments no longer guarantee the same result. Reach for it when the memory is the point, not to avoid passing a value in.

Shared Variables

Defining and Declaring with Extern

A program built from several source files sometimes needs one variable shared between them. It can be defined in only one place, where the storage is created. Here, totals dot cpp defines grand total. Every other file that wants to use it says so with extern. Report dot cpp declares it: no storage, just a reference to that one. That distinction is the whole idea. A definition creates the thing; a declaration only announces its name and type, so the compiler can check every use. Exactly one definition, any number of declarations. Built together, the program prints 12. Shared variables like this should be rare. Anything reachable from every file can be changed from every file, so a wrong value gives no clue where it came from. Pass values through parameters.

Keyword Notes

File-Level Static and the Retired Register

Two more keyword notes, so old code causes no confusion. The same word, static, does two unrelated jobs. Written on a local, it changes the lifetime. Written on a variable or function outside all functions, it doesn't change the lifetime, which is already static. It restricts the name to the file it appears in. And register. A processor keeps a small number of extremely fast storage locations of its own, called registers. The register keyword was once a hint that a variable should live in one. Compilers allocate registers by analyzing the whole function, and do it better than a programmer's intuition. The hint was routinely ignored, and it was removed from the language. Don't write it.

Mechanism

The Call Stack and Its Frames

Automatic variables have to live somewhere, and where they live explains their behavior completely. The mechanism is the call stack: a region of memory used strictly last in, first out, like a stack of plates where only the top one is reachable. Each time a function is called, a block of memory called a stack frame is pushed onto the top. When the function returns, its frame is popped off.

Definition

What a Stack Frame Holds

A frame holds the parameters, copied in at the call. The local variables. The return address, recording where in the caller to resume. And saved bookkeeping, whatever the machine must restore so the caller finds things as it left them. Everything that call needs, and nothing that outlives it.

Walkthrough

Frames Pushed and Popped in Calls

Watch the frames. Main runs, with its own frame. Main calls process, and process's frame is pushed on top. Process calls helper, and helper's frame goes on top. Only the top frame is active. When helper returns, its frame is popped, and its locals no longer exist. The return address is how control finds its way back down. Everything about automatic variables now follows. They're fresh on each call, because each call gets a new frame. They vanish on return, because the frame is popped. And two calls to the same function never interfere, because their frames are separate. That's also why a function can call itself without confusion.

Common Mistake

Why Returning a Local Reference Fails

Now it's clear why returning a reference to a local is fatal. Here, total lives in make total's frame. The frame is popped the moment the function returns. A reference handed back therefore refers to memory that's no longer in use, and will be reused by the very next call. The compiler warns, as shown. This is undefined behavior: it often produces a value that looks plausible for a while, and then doesn't. Return by value instead. It's the same rule you met with strings, and now you can see exactly why it holds.

Limits

Deep Recursion Exhausts the Stack

The stack isn't unlimited. A fixed region is set aside when the program starts, commonly around 1 to 8 megabytes, and every unreturned call occupies part of it. A function that calls itself pushes a new frame each time, and pops none until the innermost call returns. So depth is paid for in memory. A loop running 10 million times uses one frame. Recursion 1,000 deep is comfortable: it prints 1000. Recursion a million deep exhausts the stack, and the system stops the program. That's a stack overflow. Recursion with no stopping case fails within a fraction of a second.

Comparison

Endless Loop Versus Endless Recursion

Put the first and last cases side by side. An endless loop runs forever, consuming no extra memory. An endless recursion consumes memory on every pass, and is killed almost immediately. Knowing the difference tells you which mistake you've made, before you even open the code.

New Concept

Const Versus Constexpr Values

Some values are settled before the program ever runs, and saying so lets the compiler do the work. A plain int guarantees nothing. It may change later. A const int won't be modified after it's set, but its value may not be known until the program runs, because it might come from input. A constexpr int won't be modified, and its value must be computable during compilation. The compiler rejects anything it can't work out then, as shown. So a constexpr value can be used wherever the language demands a known figure, like the size of a fixed array. Use const to say a value must not change, and constexpr when it must also be known before the program starts.

Quick Check

Moving the Counter Outside the Function

Quick check. Take the plain tick, and move its int calls = 0 declaration outside every function, above them all. What do three calls to tick print now? Pause and predict. 1, 2, 3. A variable outside every function has static storage duration: one counter, created once, for the whole run. What changed, compared with a static local, is scope. Any function below it in the file can now see it, and change it.

Key Takeaways

Scope, Duration, Frames, and Constexpr Recap

To summarize. Scope is where a name can be used. Storage duration is how long its storage exists. Ordinary locals are automatic: fresh on every call, and gone on return. Static locals remember, at a cost in predictability. Each call pushes a stack frame, and each return pops it, which is why deep recursion can run out of room. And constexpr means a value is also known before the program runs.

About C++ Programming at UPI Study

C++ Programming (Computer Science 242) is a self-paced online course of 84 video lessons in 11 chapters, from your first program to decisions, loops, arrays, functions, classes, files, pointers and inheritance. It carries a NCCRS credit recommendation for 3 semester credits.

Every lesson transcript is free on pages like this one, and the videos are free with a free UPI Study account. Enrolling adds a practice quiz after each lesson, the graded work and final exam that earn the credit, lifetime access, and an official transcript on completion. UPI Study has helped over 130,000 students with college credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Why take it here

Why students take C++ Programming with UPI Study

Your first real programming languageC++ teaches how programs really work: variables, control flow, functions, memory and objects. It is the base for data structures, algorithms, systems and game programming, and for most computer-science degrees.
Credit at a fraction of the priceThe same 3 credits can cost $750 or more at a US college. Here it is one payment, self-paced, with lifetime access.
Finish your degree soonerTransfer credits reduce the courses you still owe, so you can graduate earlier or lighten a heavy semester.
Strengthen an applicationA completed college-level course on an official transcript shows admissions and scholarship committees you can do the work.
Study on your own scheduleNo start dates, no deadlines. Watch the lessons, practice, and take the final when you are ready.
130,000+students helped with college creditsACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Where the credit goes

Universities that work with NCCRS

C++ Programming carries a NCCRS credit recommendation. These universities work with NCCRS; some are UPI Study's signed partners. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Find my college → Not listed? Ask Athena in the corner of this page, or book a 15-minute call.

FAQ

Questions students ask us

About How the Compiler Manages Variable Storage

What is the difference between scope and lifetime of a variable in C++?
Scope is where a name can be used, set by where the declaration sits in the source, while lifetime (storage duration) is how long the variable's storage exists. They usually match for ordinary locals. A static local separates them: its name is visible only inside one function, yet its storage lasts for the whole program run.
What does static do to a local variable in C++?
Writing static before a local variable changes its storage duration, not its scope. The variable is created once, before first use, and lives until the program ends. Its initialization runs only the first time. A counter like static int calls = 0; incremented on three calls prints 1, 2, 3, while the automatic version prints 1, 1, 1.
What happens on the call stack when a function is called and when it returns?
Each call pushes a stack frame onto the top of the call stack, and each return pops it off. The frame holds the parameters, the local variables, the return address, and saved bookkeeping. This is why locals start fresh on every call and vanish on return, and why returning a reference to a local is undefined behavior.
What is the difference between const and constexpr in C++, and what causes a stack overflow?
A const value won't change after it is set, but it may not be known until runtime, such as one read from input. A constexpr value also must be computable at compile time, so it can size a fixed array. A stack overflow happens when too many unreturned calls, such as endless recursion, use up the fixed stack memory.

About the C++ Programming course

How many college credits is C++ Programming?
C++ Programming (Computer Science 242) carries a NCCRS credit recommendation for 3 semester credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
How much does C++ Programming cost and what is included?
$250, paid once, with lifetime access: all 84 video lessons, a practice quiz after each, the graded work and final exam, and an official transcript when you finish. Students who want several courses choose UPI Study PRO instead.
Which universities accept UPI Study credits?
UPI Study has signed partner agreements with universities such as Thomas Edison State University, University of the People and Charter Oak State College, and works with universities such as Penn State, SNHU and SUNY. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities. Use "Find my college" on this page to check yours.
How long does C++ Programming take?
It is self-paced with no deadlines. The 84 lessons total about 0 hours of video; most students add reading and practice time and finish in a few weeks to a few months, on their own schedule.
How are the exams done?
Quizzes and assignments are online in the course. The final exam is taken online with proctoring; a person on the UPI Study team checks assignments, usually within two days.
How do my credits reach my college?
When the course shows 100% complete, request the official transcript from your dashboard. It is sent to the college you name, and you can send it to more than one. Check your college first with "Find my college" on this page, or ask Athena.

Ready for the full course? Earn 3 credits in C++ Programming.

84 video lessons, a quiz after each one, and the graded work that earns the credit. Recommended for credit by NCCRS.

C++ Programming
3 credits · NCCRS
Enroll →