What is undefined behavior (UB) in C and C++? What about unspecified behavior and implementation-defined behavior? What is the difference between them?
C++: Undefined, Unspecified, and Implementation-Defined Behavior Explained
c++implementation-defined-behaviorundefined-behaviorunspecified-behavior
Related Solutions
No, unless you're also keeping your compiler the same and your compiler documentation defines the otherwise undefined behavior.
Undefined behavior means that your compiler can ignore your code for any reason, making things true that you don't think should be.
Sometimes this is for optimization, and sometimes it's because of architecture restrictions like this.
I suggest you read this, which addresses your exact example. An excerpt:
Signed integer overflow:
If arithmetic on an
int
type (for example) overflows, the result is undefined. One example is thatINT_MAX + 1
is not guaranteed to beINT_MIN
. This behavior enables certain classes of optimizations that are important for some code.For example, knowing that
INT_MAX + 1
is undefined allows optimizingX + 1 > X
totrue
. Knowing the multiplication "cannot" overflow (because doing so would be undefined) allows optimizingX * 2 / 2
toX
. While these may seem trivial, these sorts of things are commonly exposed by inlining and macro expansion. A more important optimization that this allows is for<=
loops like this:for (i = 0; i <= N; ++i) { ... }
In this loop, the compiler can assume that the loop will iterate exactly
N + 1
times ifi
is undefined on overflow, which allows a broad range of loop optimizations to kick in. On the other hand, if the variable is defined to wrap around on overflow, then the compiler must assume that the loop is possibly infinite (which happens ifN
isINT_MAX
) - which then disables these important loop optimizations. This particularly affects 64-bit platforms since so much code usesint
as induction variables.
C standards define UsB, UB and IDB in a way that can be summarized as follows:
Unspecified Behavior (UsB)
This is a behavior for which the standard gives some alternatives among which the implementation must choose, but it doesn't mandate how and when the choice is to be made. In other words, the implementation must accept user code triggering that behavior without erroring out and must comply with one of the alternatives given by the standard.
Be aware that the implementation is not required to document anything about the choices made. These choices may also be non-deterministic or dependent (in an undocumented way) on compiler options.
To summarize: the standard gives some possibilities among which to choose, the implementation chooses when and how the specific alternative is selected and applied.
Note that the standard may provide a really large number of alternatives. The typical example is the initial value of local variables that are not explicitly initialized. The standard says that this value is unspecified as long as it is a valid value for the variable's data type.
To be more specific consider an int
variable: an implementation is free to choose any int
value, and this choice can be completely random, non-deterministic or be at the mercy of the whims of the implementation, which is not required to document anything about it. As long as the implementation stays within the limits stated by the standard this is ok and the user cannot complain.
Undefined Behavior (UB)
As the naming indicates this is a situation in which the C standard doesn't impose or guarantee what the program would or should do. All bets are off. Such a situation:
renders a program either erroneous or nonportable
doesn't require absolutely anything from the implementation
This is a really nasty situation: as long as there is a piece of code that has undefined behavior, the entire program is considered erroneous and the implementation is allowed by the standard to do everything.
In other words, the presence of a cause of UB allows the implementation to completely ignore the standard, as long as the program triggering the UB is concerned.
Note that the actual behavior in this case may cover an unlimited range of possibilities, the following is by no means an exhaustive list:
- A compile-time error may be issued.
- A run-time error may be issued.
- The problem is completely ignored (and this may lead to program bugs).
- The compiler silently throws the UB-code away as an optimization.
- Your hard disk may be formatted.
- Your computer may erase your bank account and ask your girlfriend for a date.
I hope the last two (half-serious) items can give you the right gut-feeling about the nastiness of UB. And even though most implementations will not insert the necessary code to format you hard drive, real compilers do optimize!
Terminology Note: Sometimes people argue that some piece of code which the standard deems a source of UB in their implementation/system/environment work in a documented way, therefore it cannot be really UB. This reasoning is wrong, but it is a common (and somewhat understandable) misunderstanding: when the term UB (and also UsB and IDB) is used in a C context it is meant as a technical term whose precise meaning is defined by the standard(s). In particular the word "undefined" loses its everyday meaning. Therefore it doesn't make sense to show examples where erroneous or nonportable programs produce "well-defined" behavior as counterexamples. If you try, you really miss the point. UB means that you lose all the guarantees of the standard. If your implementation provides an extension then your guarantees are only those of your implementation. If you use that extension your program is no more a conforming C program (in a sense, it is no more a C program, since it doesn't follow the standard any longer!).
Usefulness of undefined behavior
A common question about UB is something on these lines: "If UB is so nasty, why does not the standard mandate that an implementation issues an error when faced with UB?"
First, optimizations. Allowing implementations not to check for possible causes of UB allows lots of optimizations that make a C program extremely efficient. This is one of the features of C, although it makes C a source of many pitfalls for beginners.
Second, the existence of UB in the standards allows a conforming implementation to provide extensions to C without being deemed non-conforming as a whole.
As long as an implementation behaves as mandated for a conforming program, it is itself conforming, although it may provide non-standard facilities that may be useful on specific platforms. Of course the programs using those facilities will be nonportable and will rely on documented UB, i.e. behavior that is UB according to the standard, but that an implementation documents as an extension.
Implementation-defined Behavior (IDB)
This is a behavior that can be described in a way similar to UsB: the standard provides some alternatives and the implementation choose one, but the implementation is required to document exactly how the choice is made.
This means that a user reading her compiler's documentation must be given enough information to predict exactly what will happen in the specific case.
Note that an implementation that doesn't fully document an IDB cannot be deemed conforming. A conforming implementation must document exactly what happens in any case that the standard declares IDB.
Examples of unspecified behavior
Order of evaluation
Function arguments
The order of evaluation for function arguments is unspecified EXP30-C.
For instance, in c(a(), b());
it is unspecified whether the function a
is called before or after b
. The only guarantee is that both are called before the c
function.
Examples of undefined behavior
Pointers
Dereferencing of null pointer
Null pointers are used to signal that a pointer does not point to valid memory. As such, it does not make much sense to try to read or write to memory via a null pointer.
Technically, this is undefined behaviour. However, since this is a very common source of bugs, most C-environments ensure that most attempts to dereference a null pointer will immediately crash the program (usually killing it with a segmentation fault). This guard is not perfect due to the pointer arithmetic involved in references to arrays and/or structures, so even with modern tools, dereferencing a null pointer may format your hard drive.
Dereferencing of uninitialized pointer
Just like null pointers, dereferencing a pointer before explitely setting its value is UB. Unlike for null pointers, most environments do not provide any safety net against this sort of error, except that compiler can warn about it. If you compile your code anyway, you'll are likely to experience the whole nastiness of UB.
Dereferencing of invalid pointers
An invalid pointer is a pointer that contains an address that is not within any allocated memory area. Common ways to create invalid pointers is to call free()
(after the call, the pointer will be invalid, which is pretty much the point of calling free()
), or to use pointer arithmetic to get an address that is beyond the limits of an allocated memory block.
This is the most evil variant of pointer dereferencing UB: There is no safety net, there is no compiler warning, there is just the fact that the code may do anything. And commonly, it does: Most malware attacks use this kind of UB behaviour in programs to make the programs behave as they want them to behave (like installing a trojan, keylogger, encrypting your hard drive etc.). The possibility of a formatted hard drive becomes very real with this kind of UB!
Casting away constness
If we declare an object as const
, we give a promise to the compiler that we will never change the value of that object. In many contexts compilers will spot such an invalid modification and shout at us. But if we cast the constness away as in this snippet:
int const a = 42;
...
int* ap0 = &a; //< error, compiler will tell us
int* ap1 = (int*)&a; //< silences the compiler
...
*ap1 = 43; //< UB ==> program crash?
the compiler might not be able to track this invalid access, compile the code to an executable and only at run time the invalid access will be detected and lead to a program crash.
category 2
put a title here!
put your explanation here!
Examples of implementation-defined behavior
category 1
put a title here!
put your explanation here!
Best Answer
Undefined behavior is one of those aspects of the C and C++ language that can be surprising to programmers coming from other languages (other languages try to hide it better). Basically, it is possible to write C++ programs that do not behave in a predictable way, even though many C++ compilers will not report any errors in the program!
Let's look at a classic example:
The variable
p
points to the string literal"hello!\n"
, and the two assignments below try to modify that string literal. What does this program do? According to the C++ standard, [lex.string] note 4, it invokes undefined behavior:I can hear people screaming "But wait, I can compile this no problem and get the output
yellow
" or "What do you mean undefined, string literals are stored in read-only memory, so the first assignment attempt results in a core dump". This is exactly the problem with undefined behavior. Basically, the standard allows anything to happen once you invoke undefined behavior (even nasal demons). If there is a "correct" behavior according to your mental model of the language, that model is simply wrong; The C++ standard has the only vote, period.Other examples of undefined behavior include
i++ + ++i
.[intro.defs] also defines undefined behavior's two less dangerous brothers, unspecified behavior and implementation-defined behavior:
What can you do to avoid running into undefined behavior? Basically, you have to read good C++ books by authors who know what they're talking about. Avoid internet tutorials. Avoid bullschildt.