When the C (or C++) standard says “doing X is undefined behavior”, they’re not telling you that your program will exit with an error, corrupt your memory, or even crash for that matter. These are only a few of ways in which undefined behavior can manifest.

There is a reason they are called “nasal demons”. In principle, undefined behavior could wipe your entire hard drive.

Of course, in the real world, compilers are too lazy to actually make your program erase the disk. Instead, they tend to take simpler route and assume that undefined behavior is “impossible”.

It doesn’t matter if it’s actually not impossible. The compilers don’t care. They want the most optimal code, and they will get the most optimal code even if it means wreaking havoc on your program.

Consider the following example:

Suppose you don’t pass any arguments to this program (i.e. argc == 1). In this case, the pointer returned by getnumptr points to n, which contains 42, so you should expect 42 to be printed.

However, if you do pass arguments to the program (i.e. argc > 1), then you’re in the realm of undefined behavior. On some compilers, the program will crash with a segmentation fault, which is probably what most people expect.

On less forgiving compilers however, the program may simply print 42.

How could one get a 42 by dereferencing a NULL pointer?

Well, the compiler was free to assume that the situation of dereferencing a NULL pointer was “impossible”, and hence didn’t bother to emit the code for that case at all. The if block was entirely elided from the generated assembly. All that remained was:

When they say undefined behavior means anything goes, they really mean it.

This article is brought to you by undefined behavior.