33 Intermediate The Hundred
The Struct That Forgot Its Zero
two points, two braces apart
struct Point {
int x;
int y;
};
void show_canvas() {
Point size{800, 600};
std::cout << "canvas: " << size.x << " x " << size.y << "\n";
}
void show_points() {
Point a; // no constructor to run, so... zero?
Point b{};
std::cout << "a = (" << a.x << ", " << a.y << ")\n";
std::cout << "b = (" << b.x << ", " << b.y << ")\n";
}
int main() {
show_canvas();
show_points();
}
Run it. Are a and b the same point?
Answer
No. Typical output (GCC 11.5 and Clang 18.1, x86-64, no -O flag) — a varies by build:
Why¶
Point a; is default-initialization: it runs the implicit default constructor, which for
these two ints is trivial and does nothing at all, so a keeps whatever bytes were
there and reading them is undefined behavior. Point b{} value-initializes, zeroing
both; the heap splits the same way (new Point vs new Point()). Stale bytes are no
promise: at -O0 a inherits show_canvas's size, but from -O1 up GCC exploits the
undefined read and prints a = (0, 0), while Clang prints stack junk — a different number
under Valgrind than without. One twist: a user-provided Point() {} stops b{} zeroing.
C++26 (P2795) reclassifies this read as erroneous behaviour rather than UB — an
implementation-supplied value, diagnostics encouraged. GCC's -Wall -Wextra flags it
(-Wmaybe-uninitialized at -O0, -Wuninitialized above); Clang 18.1 says nothing at any
level, and Valgrind catches both -O0 builds but neither optimized one.
The fix¶
Point a{}; // value-initialized: (0, 0), every time
struct Point { int x = 0; int y = 0; }; // or bake the zeros into the type
Takeaway: {} is what turns default-initialization into value-initialization.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo, then again with -O1