Renders the entire program meaningless if certain rules of the language are violated.
The C++ standard precisely defines the observable behavior of every C++ program that does not fall into one of the following classes:
std::size_t
or the number of bits in a byte, or the text of std::bad_alloc::what
. A subset of implementation-defined behavior is locale-specific behavior, which depends on the implementation-supplied locale. Because correct C++ programs are free of undefined behavior, compilers may produce unexpected results when a program that actually has UB is compiled with optimization enabled:
For example,
int foo(int x) { return x + 1 > x; // either true or UB due to signed overflow }
may be compiled as (demo).
foo(int): movl $1, %eax ret
int table[4] = {}; bool exists_in_table(int v) { // return true in one of the first 4 iterations or UB due to out-of-bounds access for (int i = 0; i <= 4; i++) if (table[i] == v) return true; return false; }
May be compiled as (demo).
exists_in_table(int): movl $1, %eax ret
std::size_t f(int x) { std::size_t a; if (x) // either x nonzero or UB a = 42; return a; }
May be compiled as (demo).
f(int): mov eax, 42 ret
The output shown was observed on an older version of gcc.
#include <cstdio> int main() { bool p; // uninitialized local variable if (p) // UB access to uninitialized scalar std::puts("p is true"); if (!p) // UB access to uninitialized scalar std::puts("p is false"); }
Possible output:
p is true p is false
int f() { bool b = true; unsigned char* p = reinterpret_cast<unsigned char*>(&b); *p = 10; // reading from b is now UB return b == 0; }
May be compiled as (demo).
f(): movl $11, %eax ret
int foo(int* p) { int x = *p; if (!p) return x; // Either UB above or this branch is never taken else return 0; } int bar() { int* p = nullptr; return *p; // Unconditional UB }
may be compiled as (foo with gcc, bar with clang).
foo(int*): xorl %eax, %eax ret bar(): retq
std::realloc
Choose clang to observe the output shown.
#include <cstdlib> #include <iostream> int main() { int *p = (int*)std::malloc(sizeof(int)); int *q = (int*)std::realloc(p, sizeof(int)); *p = 1; // UB access to a pointer that was passed to realloc *q = 2; if (p == q) // UB access to a pointer that was passed to realloc std::cout << *p << *q << '\n'; }
Possible output:
12
Choose clang or the latest gcc to observe the output shown.
#include <iostream> bool fermat() { const int max_value = 1000; // Endless loop with no side effects is UB for (int a = 1, b = 1, c = 1; true; ) { if (((a * a * a) == ((b * b * b) + (c * c * c)))) return true; // disproved :) a++; if (a > max_value) { a = 1; b++; } if (b > max_value) { b = 1; c++; } if (c > max_value) c = 1; } return false; // not disproved } int main() { std::cout << "Fermat's Last Theorem "; fermat() ? std::cout << "has been disproved!\n" : std::cout << "has not been disproved.\n"; }
Possible output:
Fermat's Last Theorem has been disproved!
C documentation for Undefined behavior |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/ub