-1

C code related to the question:

#include <stdio.h>

int main(int argc, char **argv)
{
    int k = 0;
    double b = k++ + ++k + k--;
    printf("%d", k);
    return 0;
}

The value stored in b is undefined, but what about k?

The place where I found this:

http://www.sanfoundry.com/online-c-test-precedence-order-evaluation/ Question #10

--EDIT--

What I found so far: The value stored in b is not used anywhere, so if storing something into b would be the only UB, this program would not depend on UB.

But I also found this part in C99 6.5.2:

"Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression."

And listed under J.2. Undefined behavior:

" The behavior is undefined .... ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint is violated"

But the actual question is not answered yet.

-- EDIT #2 --

Not that I'm trying to write one, but a ''A strictly conforming program'' according to the standard :

"shall not produce output dependent on any unspecified, undefined, or implementation-defined behavior"

So the orignal example was wrong, since it did depend on undefined behaviour, it would be undefined even if one would replace the line

double b = k++ + ++k + k--;

with the line

    k++ + ++k + k--;

So right now I'm looking for a better presentation of what question is about.

Gábor Buella
  • 1,840
  • 14
  • 22
  • 3
    Undefined is undefined, plain and simple. – T.C. Jul 25 '14 at 16:01
  • the effects of undefined behavior are undefined :-( You can ask - what are the common behaviors seen in specific situations. For example 'is this code likely to crash', for your sample I would say 'no' but there's not guarantee – pm100 Jul 25 '14 at 16:01
  • The short answer is: NO, side effects of undefined behavior are NOT defined. It's undefined behavior. Says so right in the name. Once you hit undefined behavior, ALL BETS ARE OFF. – Michael Kohne Jul 25 '14 at 16:01
  • The reason the value of `b` is undefined is *because* the operations on `k` are undefined. So yes, `k` is undefined too. – Kninnug Jul 25 '14 at 16:02
  • `k` should be always 1 in this case! – Sathish Jul 25 '14 at 16:02
  • 2
    @Sathish What makes you assert that? – devnull Jul 25 '14 at 16:03
  • The value of `b` **is** the side effect of the undefined behavior invoked by the manipulation on `k`. – jxh Jul 25 '14 at 16:06
  • @devnull use associativity rules! right to left! – Sathish Jul 25 '14 at 16:06
  • @MichaelKohne, @T.C., while there is no answer in general, wouldn't you be able to answer the question in some particular instances? It seems to me that the value of `k` in OPs example is perfectly defined. You don't have a sequence point in `double b = ...` and hence can't predict that value of `b`. However, the end result of `k` is independent of the order in which those instructions were executed. So, why is the value of `k` UB? – Pradhan Jul 25 '14 at 16:07
  • 3
    @Pradhan modifying the value of a variable more than once in between sequence points is UB. – Kninnug Jul 25 '14 at 16:08
  • @devnull Here 2 increments and one decrement. if you do it in anyway also you will get 1 for `k`. – Sathish Jul 25 '14 at 16:08
  • @Sathish UB means that the expression may even crash, rendering the values irrelevant. – Joachim Isaksson Jul 25 '14 at 16:09
  • 1
    @Sathish associativity applies to the computation of the results, but not to the evaluation of the operands: the compiler can evaluate the `k++` and `k--` expressions in any order. – Kninnug Jul 25 '14 at 16:10
  • @Kninnug Ah ok. TIL :) Thanks. For anyone else who is interested, look [here](http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points). – Pradhan Jul 25 '14 at 16:11
  • @Pradhan: It's not the value which is UB (that wouldn't be UB but “the value of `k` is _unspecified_”), the whole programme has UB; this code may be compiled e.g. equivalently to `int main(void) {}` by a conforming implementation. UB somewhere means, the behavior of the _whole programme_ is undefined. – mafso Jul 25 '14 at 16:11
  • 1
    @Pradhan Conceptually, each of those would be usually a `load - in/decrement - store`, but the compiler is free to interleave those however it wants (assuming that it decides to play nice with you and not just generate code that formats your hard drive). For instance, it can do all three loads, then all three in/decrements, then all three stores. It's trivially easy to find interleaving orders that ends up storing `-1` or `0` or `2` in `k` rather than `1`. – T.C. Jul 25 '14 at 16:20
  • I still don't see this question as a duplicate of that other one. – Gábor Buella Jul 25 '14 at 16:24
  • 2
    @T.C.: With some newer compilers' attitude toward UB, the effects are far more serious than that. Suppose one piece of code would have UB if two pointers matched, but anything a compiler might "naturally" do would work; a later piece of code tests whether the pointers match and, if not, does something that would be disastrous if they did match. Under modern philosophy, a compiler would be free to assume that because the first piece of code would cause UB if the pointers match, it may do the second piece of code (whose effects would be disastrous if the pointers match) unconditionally. – supercat Jul 25 '14 at 16:28
  • @BuellaGábor I agree - the other question is about why some constructs are UB, while this one is more about what UB means – Wojtek Surowka Jul 25 '14 at 16:28
  • Yes @WojtekSurowka , so I'm still trying to make the question more clear. – Gábor Buella Jul 25 '14 at 16:32
  • @WojtekSurowka: This question could probably be considered a duplicate of some other questions related to UB, but not to the linked one. I think it's important that people wondering about UB be aware of the modern compiler concepts of "exploiting" it (wrongheadedly, IMHO, since there are many forms of UB whose behavior is defined in some implementations, and which would work identically in 99% of implementations which didn't go out of their way to break them; encouragement of compiler exploits thus breaks what would have been mostly-portable code). – supercat Jul 25 '14 at 16:36
  • @Pradhan - if you want to know what a particular compiler does, with a particular set of optimization settings, then you'll need to setup a test. There's no general way to predict. – Michael Kohne Jul 25 '14 at 17:23
  • @supercat Right, which is why there's a "assuming that it decides to play nice with you" parenthetical. – T.C. Jul 25 '14 at 17:35
  • 1
    @T.C.: Your parenthetical could be interpreted as "assuming your compiler isn't deliberately evil". My point was that a compiler which decides to omit tests that couldn't only be true unless a program engaged in Undefined Behavior might be trying to be "helpful" [make code run faster], and certainly not "deliberately evil", but the effects could be horrid nonetheless. – supercat Jul 25 '14 at 17:44
  • @supercat True, I could have made that more clear. Too bad I can't edit a comment after 5 minutes... – T.C. Jul 25 '14 at 17:48
  • There's no way in general to predict the value stored in `k`. Each of `k++`, `++k`, and `--k` may be evaluated in any order, and the side effects may be applied in any order. You can't give the compiler freedom to do that *and* demand that it always produce the same result for such an expression; hence, the behavior is left undefined, meaning the compiler is not required to produce a particular result. – John Bode Jul 25 '14 at 18:47
  • @JohnBode: In the old days, undefined behavior meant "do whatever is natural on the platform, even if the consequences might be undesirable." Unfortunately, rather than try to specify what the "natural" behaviors might be in cases where there was consensus among existing implementations, things have gone the other direction. For example, in the old days, if `i` is of type `int`, executing `j=i*2;` when `i` is more more than half the maximum `int` value would have stored a garbage value in `j` or caused a trap, but that would be about it. Nowadays, however... – supercat Jul 25 '14 at 22:24
  • ...some compilers would assume that once such a statement has executed, `i` must be less than half of the maximum integer value, and so any future code which would branch if `i` is more than half the maximum integer value may be skipped. Even if anything that old compilers could have done with `j=i*2;` would have been relatively harmless, skipping branches the way newer compilers can may have far more disastrous consequences. – supercat Jul 25 '14 at 22:27
  • @BuellaGábor undefined behaviour is undefined behaviour everywhere. The compiled code can do anything. Read [this for the zen moment of UB](https://markshroyer.com/2012/06/c-both-true-and-false/) – Antti Haapala -- Слава Україні Jul 30 '14 at 16:35

1 Answers1

5

As soon as we hit undefined behaviour, the compiler can do whatever it wants to - including formatting the disk if it has access rights. So nothing can be defined in this situation, including side effects.

Wojtek Surowka
  • 20,535
  • 4
  • 44
  • 51
  • 1
    `...formatting the disk...` I hope you're not a compiler writer. ;-) – Fiddling Bits Jul 25 '14 at 16:09
  • I wonder if that has actually ever happened. – this Jul 25 '14 at 16:10
  • @this, [Sort of](http://feross.org/gcc-ownage/) (it's implementation-defined, but probably the closest you'll find). – chris Jul 25 '14 at 16:11
  • @this It has. Or rather, disks have been encrypted by nifty little programms that were downloaded due to undefined behavior... Really, many so called security holes are nothing other than undefined behavior in a security relevant program. – cmaster - reinstate monica Jul 25 '14 at 16:13
  • @cmaster Those are programs made to exploit existing code. I'm talking about compiler generating code that does that out of pure ub; very unlikely. – this Jul 25 '14 at 16:16
  • @this Well, the compiler generated code that could be tricked into doing what the attacker wanted, like executing some code provided in a jpeg file, or what ever. The important point to note is, that the download could not have happened if the application did not have undefined behavior. The download is just one possible incarnation of undefined behavior. – cmaster - reinstate monica Jul 25 '14 at 16:20
  • `if (function_dependent_on_UB()) { printf("Hello\n"); } else { format_disk(); }` – Gábor Buella Jul 25 '14 at 16:22
  • @Wojtek : Your answer is not in line with "shall not produce output dependent on any unspecified, undefined, or implementation-defined behavior" , what you wrote implies "the compiler can do whatever it wants to" even if the program doesn't depend on parts of source code behaving undefinedly. – Gábor Buella Jul 25 '14 at 16:39
  • @FiddlingBits: On a machine with a memory-mapped floppy controller, stray memory accesses could turn on the drive motor, switch the controller to "write" mode, and move the head around. That wouldn't exactly "format" a disk, but could destroy any information stored thereon and render it useless unless or until such time as it was reformatted. – supercat Jul 25 '14 at 16:43
  • 2
    @BuellaGábor Your quote is the requirement for *strictly conforming program*. Since your program is not strictly conforming, the quote does not apply. UB is defined as "behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements" - if it imposes no requirement, the compiler can do anything. – Wojtek Surowka Jul 25 '14 at 16:45
  • That requirement is also the for conforming programs (not strict ) as well, if I read it well. But yeah, whatever that part says, there is the "Standard imposes no requirements" part. This changes my whole idea of "not depending on UB". If I put a UB somewhere in a 4000 line compilation unit, the compiler can do anything with the WHOLE compilation unit, it is a new (for me ) way of thinking about it. – Gábor Buella Jul 25 '14 at 17:10