Undefined Behaviour(UB)是否分编译期与运行期?相关术语咨询
Great question! Let's unpack this clearly, including correcting a small misconception in your examples first, then diving into the terminology and classifications.
First, a Quick Correction
Your example of int i = -1; unsigned int x = static_cast<unsigned int>(i); is not undefined behavior! The C++ standard explicitly defines this conversion: when a signed integer is cast to an unsigned integer, the result is the smallest non-negative value congruent to the original integer modulo (2^N) (where (N) is the number of bits in the unsigned type). So casting -1 to unsigned int will always yield UINT_MAX—this is fully specified behavior, no UB involved.
Core Question: Are There "Compile-Time" and "Run-Time" UB?
The C++ standard does not formally define separate categories for "compile-time" or "run-time" undefined behavior. That said, the C++ community and compiler developers commonly use informal (but widely understood) terms to distinguish UB based on when it can be triggered:
1. Run-Time Undefined Behavior
This is UB that only occurs when a specific code path executes, depending on runtime inputs, program state, or external factors. Examples include:
- Dereferencing a null pointer (only if the pointer is actually null when the dereference happens)
- Out-of-bounds array access
- Signed integer overflow (note: unsigned integer overflow is defined behavior, modulo (2^N))
- Accessing a dangling pointer
Your second example falls into this category:
void foo(int* p) { int v = *p; if (p == nullptr) return; }
Dereferencing p before checking if it's null is UB only if p is actually null when foo is called. However, compilers assume UB never occurs, so they might optimize away the if (p == nullptr) check entirely—this makes the UB feel like it's manifesting at compile time, but it's still rooted in a runtime scenario where the null pointer is dereferenced.
2. Translation-Time Undefined Behavior
A more precise term than "compile-time UB," this refers to UB that occurs during the program's translation phase (compilation, linking, preprocessing), regardless of whether the program ever runs. Examples include:
- Violating the One Definition Rule (ODR) (e.g., defining the same class with different implementations in separate translation units)
- Using a preprocessing directive with an invalid constant expression (e.g.,
#if 1 / 0) - Attempting to create a
constexprvalue that involves UB (e.g.,constexpr int x = 1 / 0;—this will fail at compile time because constant expressions cannot contain UB) - Using non-standard compiler extensions without proper enabling (where the standard doesn't cover the behavior)
Key Takeaway
While the C++ standard doesn't formalize these categories, the community uses run-time undefined behavior and translation-time undefined behavior as standard informal terms to discuss when UB can be triggered. The core definition of UB remains consistent across both: behavior that the C++ standard does not specify, giving compilers full license to handle it however they see fit (including optimizing code away, crashing, or producing unexpected results).
内容的提问来源于stack exchange,提问作者Daniel Stephens

