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
retint 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
retstd::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
retThe 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
retint 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():
retqstd::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