12

Specifically in reference to: https://blogs.msdn.microsoft.com/oldnewthing/20140627-00/?p=633/

I'm a new C++ programmer and I'm currently learning about undefined behavior and its effects on a program. I was linked specifically to the above blog, which says that when undefined behavior occurs, anything can happen.

It mentions several times specifically that the compiler can allow anything to happen when undefined behavior occurs.

What specifically causes this to occur, and why does it happen?

cmaher
  • 5,100
  • 1
  • 22
  • 34
Yvain
  • 261
  • 2
  • 11
  • 2
    Because undefined behavior is [undefined](http://eel.is/c++draft/defns.undefined). – nwp Feb 28 '18 at 15:04
  • 3
    That phrase is a little over dramatic. Chandler Carruth has a really good talk on this:https://www.youtube.com/watch?v=yG1OZ69H_-o – NathanOliver Feb 28 '18 at 15:05
  • 1
    An Operating System typically has its own opinions about this. It will generally enforce its own rules, but those are typically more lax than that of C++. One major exception to that rule would be Linux, which kills processes where C++ mandates a `std::bad_alloc` exception. – MSalters Feb 28 '18 at 15:13
  • @MSalters: Interesting. Could you give an example of a case where C++ mandates `std::bad_alloc` but Linux kills the process instead? – Lightness Races in Orbit Feb 28 '18 at 15:15
  • 2
    @LightnessRacesinOrbit I believe MSalters is referring to [optimistic memory allocation](https://stackoverflow.com/questions/1655650/linux-optimistic-malloc-will-new-always-throw-when-out-of-memory) which is used as the default by many Linux distributions. Allocations always succeed, but accessing that memory for the first time might fail. It's very difficult to reconcile that behavior with the requirements of c++. – François Andrieux Feb 28 '18 at 15:23
  • @FrançoisAndrieux: Terrible! – Lightness Races in Orbit Feb 28 '18 at 15:32
  • 2
    Undefined behavior *can* be serious, depending on the circumstances. Suppose that later you get a job at the Pentagon and instead of `if (door_is_open)` you have to code `if (incoming_missiles_detected)`. Then just about anything *can* happen. – Bo Persson Feb 28 '18 at 15:50
  • @BoPersson Just that variable name already scares me. – Hatted Rooster Feb 28 '18 at 15:52
  • @SombreroChicken: It's only scary if it's true. – Lightness Races in Orbit Feb 28 '18 at 15:57
  • Keep in mind that C is a horribly-designed language and C++ inherited most of C's flaws. The amount of "undefined behavior" in C++ is much greater than what one would find in a well-designed language. – user3344003 Mar 01 '18 at 03:18
  • @user3344003: At the time C was standardized, most implementations would specify behaviors in most cases where the Standard did not. The problem is that the Standard made no distinction between "Implementations should do X when practical, but implementations on weird hardware where that is impractical may do something else" and "Programmers have no basis for any particular expectations, even if they're only targeting non-weird hardware". – supercat Apr 24 '18 at 22:46
  • @BoPersson my favorite example is programming a CPAP controller. If you get undefined behavior there, you can literally get demons flying from your nose. – Mark Ransom Sep 26 '22 at 14:00

2 Answers2

28

Nothing "causes" this to occur. Undefined behaviour cannot "occur". There is no mystical force that descends upon your computer and suddenly makes it create black holes inside of cats.

That anything can happen when you run a program whose behaviour is undefined, is stated as fact by the C++ standard. It's a statement of leeway, a handy excuse used by compilers to make assumptions about your code so as to provide useful optimisations.

For example, if we say that dereferencing nullptr is undefined (which it is) then no compiler needs to ever check that a pointer is not nullptr: it can just assume that a dereferenced pointer will never be nullptr, and if it's not then any consequences are the programmer's problem.

Due to the astounding complexity of compilers, some of those consequences can be rather unexpected.

Of course it is not actually true that "anything can happen". Your computer has neither the necessary physical power nor the necessary legal authority to instantiate a black hole inside of a cat. But since C++ is an abstraction, it seems only fitting that we use abstractions to teach people not to write programs with undefined behaviour. If you program rigorously, assuming that "anything can happen" if your program has undefined behaviour, then you will not be surprised by said rather unexpected consequences, and you will not be tempted to try to "control" the outcome in any way.

llllllllll
  • 16,169
  • 4
  • 31
  • 54
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • How about you make (a version of this) a community wiki question, feel like it'd help a lot of new members. – Hatted Rooster Feb 28 '18 at 15:11
  • Oh, nothing, don't get me wrong. Just feel like it'd be nice to have a standard question with answer to link to in the future, as a dup target perhaps. I think the question right now of OP is too narrow. – Hatted Rooster Feb 28 '18 at 15:12
  • Both clang and gcc, when configured for C++ mode, interpret the fact that the C++ Standard characterizes endless loops as UB as an invitation to throw ordinary laws of causality out the window. If they determine that a loop has no side effects, but won't terminate unless `x` is less than 256, they'll replace `if (x < 256) arr[x]=1;` with an unconditional `arr[x]=1;`, compete with all the hilarity that may ensue if `x` is exceeds both 256 and the size of `arr`. I'd call that much closer to "anything can happen" than most people would expect. – supercat Oct 22 '22 at 19:30
5

From the point of view of the C and C++ Standards, the fact that some situation invokes "Undefined Behavior" means nothing more nor less than that the Standard imposes no requirements on what an implementation must do in that situation in order to be conforming. It does not imply any particular judgment as to whether implementations intended for purposes on any particular platforms should be expected to behave predictably, nor whether predictable behavior in such a situation might be required to make an implementation suitable for a such purposes on such platforms.

For some reason, some compiler writers have equated "the Standard does not require X" with "there is no need for implementations to do X", without any particular regard for the purposes to which their compilers would be put, and without regard for what behaviors might be necessary to fulfill those purposes. What caused Undefined Behavior to digress to the "anything can happen" was that compiler writers interpreted it not as an acknowledgment that there may be some combinations of platform and application field where the cost of ensuring predictable behavior would exceed the benefit, and compiler writers should exercise judgment as to when that is the case, but instead as an indication that the authors of the Standard had already exercised judgment that on all combinations of platform and application field, the cost of ensuring predictable behavior would outweigh the benefits, and there's no need for compiler writers to exercise judgment because the authors of the Standard have already done so.

supercat
  • 77,689
  • 9
  • 166
  • 211