Why this matters
Many functions need to remember something between calls. A function that hands out ticket numbers must remember the last number it gave. A function that prints a warning must remember whether it already printed it. An ordinary local variable cannot do this. It is created when the function starts and destroyed when the function ends, so every call starts from nothing.
A global variable can remember a value, but any function in the file can change it. When a global has a wrong value, you must check every function that writes to it. A static local variable sits between the two. It keeps its value from one call to the next, and only the function that declares it can use its name.
This lesson starts with a function that forgets, then adds the static keyword, compares a static local with a global, and ends with the common mistakes. It uses the scope and lifetime rules from the previous lesson, Scope and lifetime. It does not explain how the compiler searches for names.
A function that forgets
Start with a function that should return a new number each time it is called. The first call should return 1, the second 2, and so on.
Press Run. No input is needed. The output is:
first: 1
second: 1
third: 1Every call prints 1. Look at what happens inside one call. The line int counter = 0; creates a new variable with the value 0. The next line adds 1, so the variable holds 1. The function returns 1, and the variable is destroyed. When the next call starts, the line int counter = 0; runs again and creates a brand new variable. The value 1 from the previous call is gone.
A local variable lives for one call. That is the right behaviour for most local variables. The counter in this example needs to live for every call, so a different kind of variable is needed.
The static keyword
Put the word static in front of the variable's type:
The output is:
first: 1
second: 1
third: 1The output is now 1, 2, 3. Only one word changed. The word static gives the variable three properties.
- Scope. The name
countercan be used only insidenext_id, the same as an ordinary local variable. Code inmaincannot read or change it. - Initialisation happens once. The
= 0runs only the first time the function is called. Later calls skip that line and use the value that is already stored. - Lifetime. The variable is not destroyed when the function returns. It exists from the first call until the program ends.
A trace table shows the difference. Read it from the top, one call per row.
call value before the call value after the add returned
1 (created with 0) 1 1
2 1 2 2
3 2 3 3The "value before the call" column for call 2 is 1, because call 1 left that value behind. Without static, that column would be 0 on every row, because each call creates a new variable.
The word static must go inside the function body, in front of the variable's type. The word has other meanings in C++ when it is placed elsewhere, and this lesson does not cover them.
Static local versus global
A global variable is declared outside every function. A static local is declared inside one function with static. Both keep their values between calls. The next program has one of each, plus a plain local variable for comparison.
The output is:
plain: 1
static: 1
global: 1
plain: 1
static: 2
global: 2
plain: 1
static: 3
global: 3
main sees total_calls = 3The plain counter prints 1 every time, as in the opening example. The static counter and the global both count up. The difference is in who can reach them.
main can read total_calls because it is a global. It can see the global from any function in the file after the line where it is declared. main cannot read the static local count, because that name exists only inside static_counter.
The two differ in when they are set up, too. A global is set up once, before main starts. A static local is set up the first time its function runs.
A simple way to keep the two apart:
| Global variable | Static local variable | |
|---|---|---|
| Where its name can be used | Any function after its declaration | Only the function that declares it |
| Keeps its value between calls | Yes | Yes |
| Who can change it | Any function in the file | Only that function |
Think of a global as a whiteboard in a hallway. Anyone who walks past can write on it, and when something on it is wrong, you have to ask everyone who walked past. A static local is more like a notebook that one clerk keeps in a drawer. Only that clerk can open it, and the notes stay in the notebook between visits. If the notebook has a wrong number, you only need to look at what that clerk does.
This is the reason to choose a static local when the value belongs to one function. The function keeps its own memory, and nothing else in the program can damage it.
The initialiser runs once
The part after the = in a static variable's declaration is called the initialiser. In the earlier examples the initialiser was the number 0. It can also be a call to another function. This matters because the call runs only once.
The output is:
load_limit runs
request 1: allowed
request 2: allowed
request 3: allowed
request 4: denied
request 5: deniedThe line static int limit = load_limit(); is inside allow_request, which runs five times. The message "load_limit runs" appears only once, at the start, because the initialiser runs only on the first call. Each later call skips that line and reuses the stored value 3.
The line static int used = 0; works the same way. The count used starts at 0 once, then goes up by one each time a request is allowed. The function allows three requests, then refuses the rest. The limit stays at 3 for the whole run.
Since C++11, the language also guarantees that the initialiser runs once even when two threads call the function for the first time at the same moment. Threads are covered in the "Writing to a static local from two threads at once" mistake below. You do not need to write any extra code for this guarantee.
Notice that the code in allow_request does not need to know whether it is the first call. The static declarations take care of that. This is the main reason to use them: the function can hold a setup value and a running count without any extra flag from the caller.
A third example: warn once
A program often needs to print a message only once, even if the same code runs many times. A static bool (a variable that holds true or false) can record whether the message was already printed.
The output is:
WARNING: disk almost full
loop 1 done
loop 2 done
loop 3 doneThe first call sees shown as false, prints the warning, and sets shown to true. The second and third calls see true, so the if body is skipped. The const string& in the parameter list is covered in the earlier parameter lessons. The part to notice here is that the flag lives in the function, so main does not need its own flag.
When a static local is the right tool
A static local fits when all three of these are true:
- The value belongs to one function's job, such as a ticket count or a flag that says a message was shown.
- Nothing else in the program should be able to change it.
- The value should survive from one call to the next.
A static local is the wrong tool when the function should not remember anything. A function that adds two numbers gives the same answer for the same inputs every time, and it should have no memory. If you find yourself adding a static variable to a function that could have used parameters and a return value, stop and pass the value in instead. Memory that is hidden inside a function makes the function harder to test, because its answer depends on how many times it was called before.
A static local also takes its memory at the start of the program, whether or not the function is ever called. For one small counter that cost is negligible, so the test above is about clarity, not speed.
Common mistakes
Leaving out static
This is the most common mistake, and the opening example shows it. A variable declared without static is created on every call, so it always starts from its initial value. If a counter always prints 1, check the declaration first. The fix is the one-word change shown in the section about the static keyword.
Using the name outside the function
A static local is visible only inside its function. Trying to read it from main gives a compile error. Here is a program that tries:
#include <iostream>
using namespace std;
int next_id() {
static int counter = 0;
counter = counter + 1;
return counter;
}
int main() {
next_id();
cout << counter << endl;
return 0;
}The compiler reports this (the message is shown with the plain quote marks that g++ uses when run with LC_ALL=C):
m1.cpp: In function 'int main()':
m1.cpp:12:13: error: 'counter' was not declared in this scope
12 | cout << counter << endl;
| ^~~~~~~The message means that the name counter does not exist at the line where it is used. The fix is to print the value that the function returns, not the variable inside it. Writing cout << next_id() << endl; does that. If another function really needs the value, the function should return it. Making the variable a global would also work, but that gives up the protection that the static local provided.
Expecting a fresh start each time
A static local keeps its value for the whole run of the program. A function that should start from zero on each call needs an ordinary local variable. This program sums two lists, and the second sum is wrong:
The output is:
first list sum: 6
second list sum: 36The second sum should be 30, which is 10 plus 20. It is 36 because total still held 6 from the first call. The sum of the first list was added in, and the second list was added on top.
The fix is to remove static, so that total starts at zero on each call:
The output is:
first list sum: 6
second list sum: 30The rule to remember: a static local is for a value that should carry over between calls. A value that should be rebuilt on each call stays an ordinary local.
Assuming each caller gets its own value
A static local is one variable for the whole program. Every call to the function, from every place in the code, uses the same variable. This includes calls made from inside the same function, which happens in recursion (a function that calls itself). The next program counts how many times count_down runs:
The output is:
call 1 with n = 2
call 2 with n = 1
call 3 with n = 0
call 4 with n = 1
call 5 with n = 0The first call from main is count_down(2). It calls itself with 1, which calls itself with 0. That is three calls, and calls counts them as 1, 2 and 3. The second call from main is count_down(1), which makes two more calls, counted as 4 and 5. calls does not reset between the two calls from main, and it does not reset between the levels of recursion either.
If each level of recursion needs its own count, the variable must be an ordinary local or a parameter. A static local should only be used when one shared count across all calls is what you want.
Writing to a static local from two threads at once
A thread is a separate path of work inside a program, and several threads can run at the same time. This lesson does not teach threads. The mistake is worth knowing about because it can happen without warning. If two threads run the same function and both add to a plain static int, the two additions can overlap. Both threads read the same old value, add one, and write back. One of the increments is lost.
The standard library type std::atomic makes each update happen as one step that no other thread can interrupt. Here is a counter that uses it. It is included to show the fix. The program has one thread, so it does not show the bug itself.
The output is:
1
2The atomic type needs the #include <atomic> line. Its operations, such as ++, are safe to use from several threads. A plain int does not have this guarantee. If a static local is written by more than one thread, check the type before anything else.
Practice
Each exercise is followed by its answer. Try each one on paper or in the Run box before you read the answer.
Exercise 1. Predict the output of this program. Then run it to check.
#include <iostream>
using namespace std;
int first_count() {
static int n = 0;
n = n + 1;
return n;
}
int second_count() {
static int n = 10;
n = n + 5;
return n;
}
int main() {
first_count();
first_count();
cout << first_count() << " " << second_count() << " " << second_count() << endl;
return 0;
}Show the answer
The output is 3 15 20. The two functions each have their own static n. The name n appears twice, but the two variables do not share anything. first_count runs three times, the third time as part of the cout line, so it prints 3. second_count starts at 10, adds 5 to get 15, then adds 5 again to get 20.
Exercise 2. Write a function int next_ticket() that returns 100 on its first call, 101 on its second call, 102 on its third call, and so on. Use a static local.
Show the answer
int next_ticket() {
static int ticket = 100;
return ticket++;
}The post-increment ticket++ returns the current value and then adds 1. The first call returns 100 and leaves 101 in ticket. The second call returns 101. The output of three calls in main is 100, 101 and 102.
Exercise 3. The function below should count how many times it has been called, but it always returns 1. Change one line so that the first call returns 1, the second returns 2, and the third returns 3.
int visits() {
int count = 0;
count = count + 1;
return count;
}Show the answer
Add static in front of int count = 0;. With that change the output of three calls is 1, 2 and 3. Without static, count starts at 0 on each call, which is the same mistake as the opening example.
Exercise 4. The following program does not compile. Explain why in one sentence, and then fix it without moving the variable.
#include <iostream>
using namespace std;
int next_id() {
static int counter = 0;
counter = counter + 1;
return counter;
}
int main() {
next_id();
cout << counter << endl;
return 0;
}Show the answer
The name counter exists only inside next_id, so main cannot use it. Replace cout << counter << endl; with cout << next_id() << endl;, so that main prints the value the function returns.
Exercise 5. Write a function void record(int value) that prints the largest value it has seen so far. It should print a line on every call. Call it with 4, 9, 2 and 7. Use a static local to hold the largest value.
Show the answer
#include <iostream>
using namespace std;
void record(int value) {
static int largest = value;
if (value > largest) {
largest = value;
}
cout << "largest so far: " << largest << endl;
}
int main() {
record(4);
record(9);
record(2);
record(7);
return 0;
}The output is:
largest so far: 4
largest so far: 9
largest so far: 9
largest so far: 9The static initialiser value runs only on the first call, so largest starts at 4. Later calls change it only when a bigger value arrives.
Summary
- A local variable is created on each call and destroyed when the call ends, so it cannot keep a value between calls.
- Adding
staticin front of a local variable's type makes it keep its value for the whole run of the program. - A static local can be used only inside the function that declares it, and the initialiser runs only on the first call.
- A global variable keeps its value too, but any function can change it, so a static local is safer when only one function needs the value.
- All calls to a function share one static local, including recursive calls.
- Use a static local for a value that should carry over between calls, and an ordinary local for anything that should start fresh each time.