18 Beginner The Hundred
The Variable That Is Zero on Tuesdays
one function, called twice, disagreeing with itself
int scoreboard;
int bonus() {
int points;
for (int i = 0; i < 3; ++i)
points += 5;
return points;
}
void play_round() {
for (int i = 0; i < 32; ++i)
scoreboard += 1 + (i % 6);
}
int main() {
int before = bonus();
play_round();
int after = bonus(); // same function, same answer... right?
std::cout << "scoreboard = " << scoreboard << '\n';
std::cout << "bonus before = " << before << '\n';
std::cout << "bonus after = " << after << '\n';
}
Run it. Do the two bonus lines agree?
Answer
They don't. Typical output (GCC 11.5 on x86-64, no -O flag):
Why¶
Both accumulators forget to start at zero; only one gets away with it. scoreboard has
static storage duration, so it is zero-initialized before main — guaranteed, and the
tally comes out right. points is a local: indeterminate, and reading it is undefined
behavior. Unoptimized, bonus and play_round share a stack slot, so the second call
inherits the 32 the loop counter left there; at -O2 GCC 11.5 prints 15 on both lines
and Clang 18 identical junk on both — the value is whatever the build leaves behind.
C++26 (P2795) reclassifies this read as erroneous behaviour rather than UB — an
implementation-supplied value, diagnostics encouraged. GCC 11.5 stays silent at -O0
even with -Wall -Wextra, then reports 'points' is used uninitialized
[-Wuninitialized] from -O1 up; Clang 18 warns already at -O0.
The fix¶
Takeaway: only static and thread-local variables are zeroed for free — a plain local holds whatever the build leaves behind.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo, then again with -O2