95 Advanced The Hundred
The Function With No Return
the code that worked fine until someone turned the optimizer on
int sign(int x) {
if (x > 0)
return 1;
if (x < 0)
return -1;
} // and zero? zero, obviously
int main() {
volatile int input = 0;
int x = input;
std::cout << "x = " << x << "\n";
std::cout << "sign(x) = " << sign(x) << "\n";
}
Compile at -O0, then at -O2. What does each build print?
Answer
They disagree — and the debug build is the one that looks right. Typical output (GCC 11 on
x86-64; Clang 18 at -O0 instead traps, dying with SIGILL before it can print the sign line):
Why¶
Falling off the end of a value-returning function other than main is undefined
behavior: the standard promises nothing, least of all 0. (main is the exception:
flowing off its end means return 0;, so this demo's main is legal.) So the compiler may
assume that end is unreachable — that x is never zero. At -O0 it assumes nothing: the
missing path runs ret, handing back whatever was in eax — here x. At -O2 the
assumption runs backwards: if x can't be zero, x < 0 is just !(x > 0), so that test is
deleted and sign becomes branchless 2 * (x > 0) - 1, calling zero negative.
The fix¶
Return on every path. -Wreturn-type is on by default in C++ and folded into -Wall;
-Werror=return-type makes it error: control reaches end of non-void function.
Takeaway: outside main, a missing return promises the end is unreachable, not 0.
Try it: g++ -std=c++17 -O0 main.cpp -o demo && ./demo, then again with -O2