Namespaces
Variants
Views
Actions

Undefined behavior

From cppreference.com
< cpp‎ | language
 
 
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function declaration
inline specifier
Exception specifications (deprecated)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
decltype (C++11)
auto (C++11)
alignas (C++11)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Implicit conversions - Explicit conversions
static_cast - dynamic_cast
const_cast - reinterpret_cast
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
 

Renders the entire program meaningless if certain rules of the language are violated.

[edit] Explanation

The C++ standard precisely defines the observable behavior of every C++ program that does not fall into one of the following classes:

  • ill-formed - the program has syntax errors or diagnosable semantic errors. A conforming C++ compiler is required to issue a diagnostic, even if it defines a language extension that assigns meaning to such code (such as with variable-length arrays). The text of the standard uses shall, shall not, and ill-formed to indicate these requirements.
  • ill-formed no diagnostic required - the program has semantic errors which may not be diagnosable in general case (e.g. violations of the ODR or other errors that are only detectable at link time. The behavior is undefined if such program is executed.
  • implementation-defined behavior - the behavior of the program varies between implementations, and the conforming implementation must document the effects of each behavior. For example, the type of 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.
  • unspecified behavior - the behavior of the program varies between implementations and the conforming implementation is not required to document the effects of each behavior. For example, order of evaluation, whether identical string literals are distinct, the amount of array allocation overhead, etc. Each unspecified behavior results in one of a set of valid results.
  • undefined behavior - there are no restrictions on the behavior of the program. Examples of undefined behavior are memory accesses outside of array bounds, signed integer overflow, null pointer dereference, modification of the same scalar more than once in an expression without sequence points, access to an object through a pointer of a different type, etc. Compilers are not required to diagnose undefined behavior (although many simple situations are diagnosed), and the compiled program is not required to do anything meaningful.

[edit] UB and optimization

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 
int foo(int x) {
    return 1;
}
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");
// may be compiled to a program that prints both lines:
// p is true
// p is false
// ...or to a program that prints nothing
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;
}
// this may be compiled as
bool exists_in_table(int v)
{
    return true;
}
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
    printf("%d %d\n", *p, *q);
// this may print 1 2

[edit] External links