tags:

views:

87

answers:

9

Let's pretend my program contains a specific construct the C++ Standard states to be unspecified behavior. This basically means the implementation has to do something reasonable but is allowed not to document it. But is the implementation required to produce the same behavior every time it compiles a specific construct with unspecified behavior or is it allowed to produce different behavior in different compiles?

What about undefined behavior? Let's pretend my program contains a construct that is UB according to the Standard. The implementation is allowed to exhibit any behavior. But can this behavior differ between compiles of the same program on the same compiler with same settings in the same environment? In other words, if I dereference a null pointer on line 78 in file X.cpp and the implementation formats the drive in such case does it mean that it will do the same after the program is recompiled?

The question is... I compile the same program with the same compier in the same environment with the same compiler settings. Will construct stated to be unspecified behavior and undefined behavior produce each the same behavior on each compile or are they allowed to differ between compiles?

+1  A: 

I don't know about unspecified behaviour (but judging from the name, maybe it does the same bad/evil thing everywhere just no one really knows what exactly it does). But for the undefined behavior, i think this one could behave VERY differently across platforms or compilers. I've seen some really strange coredumps on Solaris which did not occur on Ubuntu etc.

PeterK
I'm asking about recompiling on the same system with same compiler and same settings (all settings).
sharptooth
Sorry, missed that. Anyway, i believe you cannot (or at least should not) rely on it. Its simply undefined/unspecified, which means almost anything can happen.
PeterK
+4  A: 

If it's undefined behaviour then by it's very nature what will happen is undefined, you can't rely on it to be the same under any circumstances.

Unspecified behaviour on the other hand is something left up to individual vendors to decide how to implement, if there are ambiguities in the language spec for example. This will be consistent between compiles and runs, but not necessarily between different vendors. So, for example, relying on unspecified behaviour when you only build using Visual Studio is fine but if you try and port the code to gcc it may fail or produce a different behaviour than you are expecting.

Dave
You only answered half the question. What about unspecified behavior? :)
jalf
@jalf, Updated :)
Dave
+1  A: 

That's the purpose of specifying it as undefined...it means there is no telling what will happen, either on different or even the same platform (with repeated tests).

teehoo
+1  A: 

It's worth noting that the implementation of the specified behaviour of the C++ Standard is not 100% identical across compilers, even today. Given this, it's not reasonable to expect that unspecified or undefined behaviour is compiler-agnostic. You have the best chance of writing portable code if you just stick to the Standard.

Blair Holloway
I'm not asking about compiler-agnosticism. In my question the compiler is the same each time, the settings are the same each time, the C++ code is the same each time.
sharptooth
In this case, yes, _generally_ the same inputs will generate the same output. I've seen cases where we were relying on certain side-effects in our code that failed _spectacularly_ when we changed a seemingly-unrelated piece of code, or compiler setting. (IIRC, these involved not-100%-supported effects template instantiation and strict aliasing.)
Blair Holloway
+2  A: 

Undefined behavior can vary between runs of the same program, and even between execution of the same code in the same run of the program. As an example, the value of an uninitialized (automatic) variable is undefined, and then its actual value is just whatever value that happened to be at that place in memory. Obviously, this can vary.

EDIT:

This goes for unspecified behavior too. For example, the order of evaluation of function arguments is unspecified, so if they have side effects, those side effects can occur in any order. This may print "Hi!Ho!" or "Ho!Hi!":

f( printf("Hi!"), printf("Ho!") );

This can vary between executions, too. As the standard says: "An instance of the abstract machine can thus have more than one possible execution sequence for a given program and a given input." The difference is that with undefined behavior, anything can happen: the computer can explode, reformat the disk, or whatever. If the behavior is unspecified, the computer is not allowed to explode.

There is also implementation-defined behavior, such as the value of sizeof(int). This must be the same at all times, for the same compiler.

Thomas Padron-McCarthy
This explanation is concise, reasonable and follows "show, don't tell" principle. What about unspecified behavior?
sharptooth
A: 

No, that's partly the reason undefined/implementation-defined behaviors exist in the standard. Undefined behavior isn't guaranteed to be the same between multiple compiles of the same source code on the same computer (say, with different optimization flags).

The committee clearly prefers well defined behavior. Implementation-defined behavior exists when the committee believes that multiple implementations exist for some concept, and there is no reason to prefer one over another in all cases. Undefined behavior exists when the committee believes it is too hard to keep any promises under reasonable implementations.

In many cases, undefined behavior is implemented as something without a check. The behavior is then up to the operating system, if there is one and if it notices something less-than kosher took place.

As an example, dereferencing memory that you don't own is undefined. In general the OS will kill your program if you do that. However if the stars align just right, you may manage to dereference memory that you don't own under C++'s rules (eg., you didn't get it from new, or you already deleted it) but that the OS believes you own. Sometimes you'll get a crash and sometimes you'll just corrupt memory somewhere else in your program, and sometimes you'll get away undetected (if the memory hasn't been handed back out, for instance).

Race conditions are considered undefined, and they are notorious for being different during different runs of the program. You'll probably get different behavior each time you smash your stack if your operating system does not notice.

Double deletes are undefined. Generally they lead to crashes, but the fact that they are undefined means you can't rely on things crashing.

Max Lybbert
+2  A: 

Unspecified and undefined behavior are not guaranteed to be consistent between separate runs of an already compiled program. That alone already makes the notion of consistency between separate compiles totally meaningless.

Also, it is probably worth adding that undefined behavior can manifest itself at compilation stage by preventing the program from compiling at all.

AndreyT
+1  A: 

Many such behaviours are implemented differently when compiled with different optimization levels or with or without debug mode.

mouviciel
+1  A: 

But can this behavior differ between compiles of the same program on the same compiler with same settings in the same environment?

Yes.

In other words, if I dereference a null pointer on line 78 in file X.cpp and the implementation formats the drive in such case does it mean that it will do the same after the program is recompiled?

The results of undefined behaviour are almost always caused by code emitted by the compiler interacting with the operating system and/or hardware in ways not specified by the language designer. So if you dereference a NULL pointer, what happens is really nothing to do with the compiler but will depend on how the underlying OS/hardware deals with an invalid memory access. If the OS/hardware always deals with this in a consistent manner (for example via a trap), then you could expect UB to be consistent, but this has nothing to do with the language or the compiler.

anon