34

Our class was asked this question by the C programming prof:

You are given the code:

int x=1;
printf("%d",++x,x+1);

What output will it always produce ?

Most students said undefined behavior. Can anyone help me understand why it is so?

Thanks for the edit and the answers but I'm still confused.

Stephen
  • 1,607
  • 2
  • 18
  • 40
user416279
  • 341
  • 2
  • 4

8 Answers8

38

The output is likely to be 2 in every reasonable case. In reality, what you have is undefined behavior though.

Specifically, the standard says:

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

There is a sequence point before evaluating the arguments to a function, and a sequence point after all the arguments have been evaluated (but the function not yet called). Between those two (i.e., while the arguments are being evaluated) there is not a sequence point (unless an argument is an expression includes one internally, such as using the && || or , operator).

That means the call to printf is reading the prior value both to determine the value being stored (i.e., the ++x) and to determine the value of the second argument (i.e., the x+1). This clearly violates the requirement quoted above, resulting in undefined behavior.

The fact that you've provided an extra argument for which no conversion specifier is given does not result in undefined behavior. If you supply fewer arguments that conversion specifiers, or if the (promoted) type of the argument disagrees with that of the conversion specifier you get undefined behavior -- but passing an extra parameter does not.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 2
    @Jerry: Congratulations for writing a correct and clear answer! – Gilles 'SO- stop being evil' Aug 10 '10 at 16:21
  • 3
    `The output is likely to be 2`. +1 for the *likely* word. :) – Prasoon Saurav Aug 10 '10 at 16:26
  • One reason that you are likely to get `2` in most cases is because the compiler may optimize away the un-referenced parameter. The statement would then be simplified to `printf("%d",++x)`, whose behavior is defined. – bta Aug 10 '10 at 16:26
  • @bta only if the compiler actually analyzes the format string beforehand, which is only possible if the format string is a literal, and only if the compiler is smart enough to attempt it. – 3Dave Aug 10 '10 at 16:36
  • 1
    I am sad that my english is not that good to catch that lawyer like speech in standard... But the result will be always 2, not likely 2, in any implementation that can be defined implementation. This is because the "undefined behaviour" affects x+1, which is not taken at all; and I can't imagine any impl. of stdargs, nor actual code for a printf, that would be able to "move" the undefined behaviour on ++x without broking the whole implementation in those situation that std can define as defined behaviour - i.e. without making the whole compiler unusable – ShinTakezou Aug 10 '10 at 16:40
  • @ShinTakezou: the standard is very clear that if you execute *anything* with undefined behavior, *all* the behavior of the entire program (including things that happened *before* the undefined behavior actually happened) is undefined. – Jerry Coffin Aug 10 '10 at 16:53
  • 1
    @bta: Doesn't this just mean that the third parameter could result in either 2 or 3, but that the second parameter *always* evaluates to 2? Since `x+1` does not modify `x` at all, doesn't this mean that `++x` is the only modification done to x between the sequence points, and therefore *must* evaluate to 2? In other words, since `x+1` is an expression with no side effects, isn't its presence or absence irrelevant in terms of evaluating `x++`? Technically the whole statement is still undefined behavior, but since the undefined bit is actually an ignored extra parameter with no side effects.. – kyoryu Aug 10 '10 at 16:54
  • 7
    @ShinTakezou: You are incorrect; it is entirely possible for a conforming implementation to print "42". It just is really unlikely. Nor do I trust your imagination as covering all possible compiler implementations. – David Thornley Aug 10 '10 at 16:54
  • @bta: Never mind, Gilles' answer provided a clear explanation of how you could get to printing out "3". – kyoryu Aug 10 '10 at 17:02
  • 1
    -1 This is wrong because there is no undefined behavior here, as long as `x` is not volatile -- reading a non-volatile value is NOT a side effect. When there is another side effect like this, the value read from `x` is undefined, but as that value is unused, it doesn't matter. – Chris Dodd Aug 10 '10 at 20:35
  • 4
    @Chris: Where do you see a mention of side effects in: "Furthermore, the prior value shall be read only to determine the value to be stored"? Lacking such a restriction, this applies to all code. – Jerry Coffin Aug 10 '10 at 20:42
  • This sentence sums it up pretty well: *This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification.* – detly Aug 13 '10 at 08:09
  • @detly, "this rule" refers to "Between previous and next sequence point ..."? This is another detail that makes the printf("%d", inc_x(&x), x_plus_1(x)) an undefined behaviour, instead of unspec. behaviour as another use (caf if I remember well) said... (( and, shouldn't it be "precede or follow"? )) – ShinTakezou Aug 13 '10 at 08:28
  • @ShinTakezou - it actually refers to the bit starting *"Furthermore, the prior value..."* – detly Aug 13 '10 at 08:32
  • @David Thornley ... everyone failed demonstrating anything or explaining why produced code could emit not 2; mine is not just imagination: it is logic-driven intuition; I claim there's no a compiler that doesn't print "2"; and another stronger claim is that a compiler that does not emit 2 can't exist; to be moderate, I can interpret "likely" as "99.998%", that is "always" to me anyway. And don't forget the code is wrong, and this should have been a speech not only about what theoretical std-compliant compilers do, but about what actually happens, and why. I can't find reasonable words about. – ShinTakezou Apr 23 '11 at 13:01
  • @ShinTakezou: It is currently fashionable for compilers to identify inputs which would cause programs to invoke UB, and then apply optimizations on the assumption that the program will never receive such inputs. Given `if (should_launch_missiles) launch_missiles(); else printf("%d", ++x, x+1);`, hyper-modern philosophy would dictate that an optimizing compiler should call `should_launch_missiles` unconditionally, since the Standard imposes no constraints about what the code should do if `should_launch_missiles` is false, and thus calling `launch_missiles()` in that case would be legitimate. – supercat Apr 12 '16 at 18:15
13

Any time the behavior of a program is undefined, anything can happen — the classical phrase is that "demons may fly out of your nose" — although most implementations don't go that far.

The arguments of a function are conceptually evaluated in parallel (the technical term is that there is no sequence point between their evaluation). That means the expressions ++x and x+1 may be evaluated in this order, in the opposite order, or in some interleaved way. When you modify a variable and try to access its value in parallel, the behavior is undefined.

With many implementations, the arguments are evaluated in sequence (though not always from left to right). So you're unlikely to see anything but 2 in the real world.

However, a compiler could generate code like this:

  1. Load x into register r1.
  2. Calculate x+1 by adding 1 to r1.
  3. Calculate ++x by adding 1 to r1. That's ok because x has been loaded into r1. Given how the compiler was designed, step 2 cannot have modified r1, because that could only happen if x was read as well as written between two sequence points. Which is forbidden by the C standard.
  4. Store r1 into x.

And on this (hypothetical, but correct) compiler, the program would print 3.

(EDIT: passing an extra argument to printf is correct (§7.19.6.1-2 in N1256; thanks to Prasoon Saurav) for pointing this out. Also: added an example.)

Community
  • 1
  • 1
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
  • 5
    First, printf with a format of "%d" expects one integer parameter, but you've passed two. The behavior is undefined . No it is not. The second parameter is merely evaluated but the behavior is not undefined because of this reason. Note : printf("%d %d",x++);is Undefined because number of format specifiers is greater than the number of arguments. – Prasoon Saurav Aug 10 '10 at 16:02
  • What would make it a correct (though theoretical, and so unexistant) compiler? Such a compiler would fail for printf("%d %d", x+1, x+1) too: 1) load x into r1; 2) add 1 to r1 (x+1) 3) add 1 to r1 (x+1)... ops, it's a bugged compiler... once r1 is an "alias" for x into memory, the result of eval of x+1 put in r1 again will be like ++x, that is wrong; or r1 is temporary (meaning that when compiler has to eval ++x it loads x from memory again), or the result of x+1 must not be put in r1 again – ShinTakezou Aug 10 '10 at 17:05
  • 1
    @ShinTakezou: On this (hypothetical) compiler, update instructions such as `++` and `+=` happen to be treated specially. The compiler assumes that if `x` has been loaded into `r1`, *and `x` is updated before the next sequence point*, then `r1` still contains the value of `x` by the time the update instruction is generated. – Gilles 'SO- stop being evil' Aug 10 '10 at 17:21
  • 1
    @ShinTakezou: It could easily generate different code for `x+1, x+1`, since neither of those causes a change in `x`. In fact, a good compiler would probably spot those as common subexpressions. The compiler might well want to do something different for calculating function arguments containing assignments. – David Thornley Aug 10 '10 at 17:24
  • 1
    This is simply wrong -- at step 3, the value in `r1` isn't `x`, so it can't calculate ++x by adding 1 to it. By this logic the expression `x = (x + 1) - x` might set x to 0 instead of 1 – Chris Dodd Aug 10 '10 at 20:39
  • 1
    @Chris: the compiler can generate whatever it chooses since the code has undefined behavior. In `x=(x+1)-x`, the rule used in step 3 above doesn't apply since there is no update operation. And if you were thinking of objecting `x+=x` next, the compiler always computes the rhs of the assignment before dealing with the lhs, so it generates correct code too; that reasoning doesn't apply to `printf("%d",++x,x+1)` because there is no dependency between the `++x` part and the `x+1` part. – Gilles 'SO- stop being evil' Aug 10 '10 at 21:05
  • @Gilles there's no dependency? Direct, maybe no... as side-effects, yes... and there's dependency in inc_x(&x), x_plus_1(x) ? (which is not UDB but USB as explained by caf, and as I would expect by ++x,x+1) – ShinTakezou Aug 12 '10 at 07:14
  • @Gilles and "the compiler can generate whatever it chooses since the code has undefined behavior" can't be right. It seems like programmers read std, and then say: oh, this is UDB, so let's generate odd code!! No, there must be no exceptions in code generation created purposely to create a UDB! Rather, at most UDB is a consequence, from the compiler writers PoV, of a "relaxed condition", check or "no assumption about" that are always at work. As already said, your generated code would produce wrong result even for (x+1, x+2) or whatever – ShinTakezou Aug 12 '10 at 07:27
  • @ShinTakezou: If you give me three years and a research grant, I will write a compiler that works in this way. I've already explained why the compiler generates correct output on the examples on correct code that you give. – Gilles 'SO- stop being evil' Aug 12 '10 at 12:04
  • @Gilles I won't use never your compiler since I can't trust it, it would be likely unmaintenable code full of hard to track "exceptions", ... as said, that UDB must be a consequence of something else you're a doing (and that likely gives benefits) "between" source and machine code output; it can't be engraved purposely with a specific code to handle that kind of source code! Your example code is still an impossible output for a normal compiler, since (again and again) the problem is reusing the result of eval(x+1) as x, not ++x itself (result from p.2 is used in p.3 as x) – ShinTakezou Aug 13 '10 at 07:40
  • 1
    @ShinTakezou - it would still be a standards compliant compiler, which means you can always trust it if you don't write code with undefined behavior. The UDB is not a "consequence" of something a compiler writer does, but is the potential result of writing code that does not obey the rules. – detly Aug 13 '10 at 07:59
  • @ShinTakezou: *Any* optimizer is full of hard-to-track exceptions. The benefit of an optimizer is faster/smaller code. There is no problem with my compiler: since you give it invalid input (a C program with undefined behavior), the output can be whatever it likes. – Gilles 'SO- stop being evil' Aug 13 '10 at 10:17
  • @Gilles, usually optimizer are created so that they do not make a working code into not working code; your low-level code can't be generated "mechanically" by any good compiler with or w/o aggressive optimization stage, simply since it is wrong. @detly the actual result of a code that the std defines UDB (or USB) is a consequence of how a compiler is implemented, so a consequence of something a compiler writer does (or does not). A compiler that produces such code in this specific case can't be trusted. – ShinTakezou Apr 23 '11 at 13:37
12

The correct answer is: the code produces undefined behavior.

The reason the behavior is undefined is that the two expressions ++x and x + 1 are modifying x and reading x for an unrelated (to modification) reason and these two actions are not separated by a sequence point. This results in undefined behavior in C (and C++). The requirement is given in 6.5/2 of C language standard.

Note, that the undefined behavior in this case has absolutely nothing to do with the fact that printf function is given only one format specifier and two actual arguments. To give more arguments to printf than there are format specifiers in the format string is perfectly legal in C. Again, the problem is rooted in the violation of expression evaluation requirements of C language.

Also note, that some participants of this discussion fail to grasp the concept of undefined behavior, and insist on mixing it with the concept of unspecified behavior. To better illustrate the difference let's consider the following simple example

int inc_x(int *x) { return ++*x; }
int x_plus_1(int x) { return x + 1; }

int x = 1;
printf("%d", inc_x(&x), x_plus_1(x));

The above code is "equivalent" to the original one, except that the operations that involve our x are wrapped into functions. What is going to happen in this latest example?

There's no undefined behavior in this code. But since the order of evaluation of printf arguments is unspecified, this code produces unspecified behavior, i.e. it is possible that printf will be called as printf("%d", 2, 2) or as printf("%d", 2, 3). In both cases the output will indeed be 2. However, the important difference of this variant is that all accesses to x are wrapped into sequence points present at the beginning and at the end of each function, so this variant does not produce undefined behavior.

This is exactly the reasoning some other posters are trying to force onto the original example. But it cannot be done. The original example produces undefined behavior, which is a completely different beast. They are apparently trying to insist that in practice undefined behavior is always equivalent to unspecified behavior. This is a totally bogus claim that only indicate the lack of expertise in those who make it. The original code produces undefined behavior, period.

To continue with the example, let's modify the previous code sample to

printf("%d %d", inc_x(&x), x_plus_1(x));

the output of the code will become generally unpredictable. It can print 2 2 or it can print 2 3. However note that even though the behavior is unpredictable, it still does not produce the undefined behavior. The behavior is unspecified, bit not undefined. Unspecified behavior is restricted to two possibilities: either 2 2 or 2 3. Undefined behavior is not restricted to anything. It can format you hard drive instead of printing something. Feel the difference.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    (I am starting another fight against blind std affection, sorry). __But__ the OP Q is: what is the output? And the output is __always__ 2. In general, `xyz(f, ++x, x+1)` is undef behaviour since we can't say which is evaluated first, and one has a side-effect (modifies x). But in this case, we know printf gets only `++x`, while `x+1` can't modify x, or we would have a broken C impl. So, the output is predictable, and it is __always__ 2 – ShinTakezou Aug 10 '10 at 17:18
  • 2
    @ShinTakezou: Incorrect. Firstly, when the behavior is *undefined*, *anything* can happen. So, your assertions about what can and what can't happen hold no water whatsoever. Secondly, even when the behavior is defined `x + 1` can do absolutely anything with `x`, as long as the effects satisfy the specification. And yes, `x + 1` *can* modify `x` as long as it reverts it to the original value afterwards. Gilles's answer has an example of such evaluation. – AnT stands with Russia Aug 10 '10 at 17:33
  • 2
    @ShinTakezou: Thirdly, you are saying that "undef behaviour since we can't say which is evaluated first". This indicates that you do not understand the difference between *undefined* and *unspecified* behavior. You mistake *undefined* behavior for the *unspecified* one and come to meaningless conclusions as the result of that mistake. Undefined behavior in this case has absolutely *nothing* to do with what is evaluated first. – AnT stands with Russia Aug 10 '10 at 17:37
  • @ShinTakezou: Finally, a valid and legal manifestation of the undefined behavior in C is simple refusal of the compiler to compile the code. Obviously, it will not "always print 2" in case of such "manifestation" :) – AnT stands with Russia Aug 10 '10 at 17:41
  • Logic fault. If x is modified and then reverted to its original value, in the middle can't be anything, so ++x will pick the unchanged x. If something can happen in the middle, (x+1, x+1) could fail too. "nothing to do with what is evaluated first" really? I don't think. It is all about it. – ShinTakezou Aug 10 '10 at 17:44
  • @AndreyT The code is compiled, what compiler are you using?! Of course, if turn on all warnings and warnings in errors, it could happen. But a lot of those are there just to help programmers catching faults in production code, not for educational purposes. It will print __always__ 2. I wait for someone finding a standard compliant C compiler that will output a different output. – ShinTakezou Aug 10 '10 at 17:50
  • and btw, it is not the first time people tell me I don't understand standard definitions of sencente like "undefined behaviour". The fact is that, if these are the consequences, I refuse to understand and use "undefined behaviour", or whatever, in the strict standard compliant way. The OP asked which is the output. The output will be always 2, even though, according to std, the code is "undefined behaviour" (and not because of order of evaluation, but for some other reason I can't catch). – ShinTakezou Aug 10 '10 at 17:56
  • 3
    @ShinTakezou: The standard places constraints on conforming programs and conforming implementations for the benefit of all users of the C language. If you're refusing to use the same definitions as everyone else for core concepts in the C language then it's hardly surprising that you disagree with most other people on whether the behaviour is undefined or whether the output is always 2 but there's no point in making that argument. Other people are starting from definitions that you aren't using. – CB Bailey Aug 10 '10 at 18:11
  • 1
    @ShinTakezou: Wrong on all accounts. Firstly, your logic is completely broken. The `(x+1, x+1)` code can't fail simply because the behavior is perfectly defined in this case. There's no point to bring in this example for that reason. The compiler can replace `x + 1` with `++x` where it is allowed, and not replace it where it is not allowed. The compilers and their writers are smart enough to realize that. Why are you trying to portray them as so woefully primitive is not clear. – AnT stands with Russia Aug 10 '10 at 18:19
  • @ShinTakezou: Secondly, the OP never asked "what is the output". The OP asked what is the right answer to the question given to the students. The right answer is "undefined behavior". Thirdly, "what compiler I'm using" is completely irrelevant. It is prefectly clear that what I said does not refer to any specific compiler. Any compiler is *allowed* to refuse to compile this code. This is already enough to make my point. – AnT stands with Russia Aug 10 '10 at 18:20
  • 2
    @ShinTakezou: As for your refusal to understand the standard terms... well, that exactly the reason you can't come up with (or accept) the correct answer to this question. – AnT stands with Russia Aug 10 '10 at 18:22
  • @AndreyT I appreciate your effort to make me smarter and I'm gonna read'em more deeply later,now I throw just quick(!) ans.The OP wrote "What output will it always produce ?", I suppose it means he wants to know the right answer to this Q.This is 2 (ok ok, it's UB, but the output is still always2).The fail of (x+1, x+1) descends from __your__ statement, not from mine!You've said x can be actually modified in x+1 too,as if it would be an important matter for UB or whatever. I've said that if it can be modified and used before reverting, __then__ (x+1, x+1) could give wrong results.>> – ShinTakezou Aug 10 '10 at 20:09
  • >> ... if the compiler changes x+1 with ++x, any next use of it would give wrong result. Compilers and creators are smart and it won't happen, unless x is not referenced anymore, in that case, who cares what x would be if "read"? The fact is that the code we're talking of uses ++x explicitly. Or are you saying UB is since x+1 could become ++x too, resulting in `printf("%d", ++x, ++x)` (since x is not used later in the sample code)? - moreover I am confident the output is always 2 (though UB...) since I believe writers are smart, and follow "human" schemes when writing compilers. – ShinTakezou Aug 10 '10 at 20:14
  • @AndreyT and about compilers; find a compiler that refuses to compile UB - even though allowed, programmers did not them? Why? It would be an improvement for code safety! Aren't compiler writers so smart to do it?... How these compilers would catch UBs? Part of my relaxness about the challenge of writing a script that pass unmodified code to a compiler only if it is not UB, is that programmatically catching all UBs is not so easy. Even this simple case... how would you write such a script? Many programmers would be interested in the product if it catchs all UBs! There's market!Let's do it! – ShinTakezou Aug 10 '10 at 20:24
  • Surely I'm dumb,but still I say UB is UD+LwoR (UndefinedDefition + Label w/o Reason).The clear statement is "The reason the for UB is that the two exprs ++x and x + 1 are modifying x and reading x for an unrelated..."; nonetheless this does not explain _why_ technically it is UB. I don't like reasonless definition. The reason can be subjective, but must exist... Now, ++x is explicitly modifying x, x+1 shouldn't (it could, but "atomically",reverting it after using the result of the modification, right?).Why UB/US if not because x+1 can read modified or unmodified value, and we can't say which? – ShinTakezou Aug 10 '10 at 20:32
  • Last thing before to go to bed (halleluja:D): let us suppose the std change so that now your code with function is not USB anymore. It tries to do the same thing of `++x, x+1` as you said. Since `++x` could be changed always into your function, and the same for `x+1` (or to say it differently, these exprs could be always wrapped), can you tell me why the standard should keep the "bare" one UDB? There's the smallest reason? Wouldn't it be a not-necessary "asimmetry"? Is it bound to the syntax parser, or rather to the "translation" into code? – ShinTakezou Aug 10 '10 at 20:48
  • 1
    @ShinTakezou: In this context, "undefined" means that the C standard does not define the behavior (in this case, it explicitly doesn't define the behavior). It doesn't need a technical explanation, although it's reasonable to ask why the Standard is the way it is. You don't have to like that state of affairs, but you really should accept it. – David Thornley Aug 10 '10 at 22:09
  • 1
    @ShinTakezou: It would be perfectly reasonable for a compiler that was built to include lots of runtime checks of correctness (like a "bounds-checking" compiler) to output `"Program Terminated: object illegally read and modified without intervening sequence point."` instead of `"2"`. Such a compiler would be a standards-conforming. – caf Aug 11 '10 at 02:37
  • @David and @caf I have not to accept it; elsewhere someone told compiler writers have to be smart; standards writer too. There must be a reason why "x++, x+1" is undefined, while "func1(&x), func2(x)" is unspecified (since the order of eval is not; and here there can be a reason since this opens the road to optimizations); USB should be also "++x, x+1", and behave like "func1(&x), func2(x)";or the next standard should fix that absolutely, unless a reasonable motivation to make it undefined is given, and here I can't read one;so these comments are a throw for a better std, or clearer standad... – ShinTakezou Aug 12 '10 at 06:36
  • ... and it sounds very logical and normal that there's no an existing compiler wrote by normal programmers that say "program terminated object illegally read and modified without intervening sequence point"... – ShinTakezou Aug 12 '10 at 06:37
  • @ShinTakezou: a C implementation doesn't have to be a compiler. It could also be a verification tool, and this output would be *desired* from a verification tool. Note also that with the OP's code, gcc 4.3.2 emits the warning "operation on ‘x’ may be undefined". – Gilles 'SO- stop being evil' Aug 12 '10 at 12:08
  • 1
    @ShinTakezou: When I googled for C Standard Rationale, I got http://www.computer-books.us/c_2.php. You may wish to read it. If you want changes to the C standard, there is work going on right now, and the documents are publicly available at http://www.open-std.org/jtc1/sc22/wg14/, and the process is open in that anybody who wants to put in the work and can afford it may join. Alternatively, you can find a Committee member and email him. The Usenet/Netnews group comp.std.c might well be in full operation still, I haven't checked. That would be another place to go. – David Thornley Aug 12 '10 at 14:21
  • @Gilles verification tools are built around the rules, but does not explain them. Nor "validate" their existance. Since that sort of rules can't be there just for fun, there must be a reason, I can't see(the parallelism does not convince me, since I can't see a scenario where it would give a UDB instead of the obvious USB); @David thanks for the refs and more. I would also stress the fact that such answers to such questions should always point to them and cite here not only the directly interested words, but also the standard defs of used expressions (UDB, USB, sequence point...) – ShinTakezou Aug 12 '10 at 14:34
  • @Gilles unluckly now I am not on my machine where I have gcc to check it, but I am almost sure to have 4.3.2 and to have compiled the code without warnings (with -std=c99 but without -Wall) – ShinTakezou Aug 12 '10 at 14:36
  • @ShinTakezou: With gcc, you need at least `-O -Wall` to get decent warnings. With no options, gcc optimizes for *compilation* speed. In spite of the name, `-Wall` doesn't mean “all warnings”, it's closer to “all warnings that absolutely everybody would want”. – Gilles 'SO- stop being evil' Aug 12 '10 at 15:20
  • @Gilles I know that -Wall means not all optimization; but according to your opinion as interpreted by me, that warning for undefined behaviour should be absolutely wanted by everybody. – ShinTakezou Aug 13 '10 at 07:51
  • @AndreyT according to my interpretation of note 3 in this [wikipedia article](http://en.wikipedia.org/wiki/Sequence_point) about sequence point, your code printf("%d %d", inc_x(&x), x_plus_1(x)) should "invoke" undefined behaviour too, and not simply unspecified behaviour - of course noone says wikipedia is right all the time; so if you are used to it, you could fix that and make wikipedia better, or this answer, and make SO better. – ShinTakezou Aug 13 '10 at 07:54
  • @ShinTakezou: I don't see how you could come to such conclusion. If you believe that there's UB here as well, you should be able to come up with an evaluation scenario in which the modification of `x` and the reading of `x` are not separated by a sequence point (or some other UB-producing problem). Can you come up with such a scenario? I don't think it is possible. – AnT stands with Russia Aug 13 '10 at 14:39
  • @AndreyT grotesquely: an optimizer stage could inline the functions, and the next step won't see the "sequence point"... according to statements in comments of other answers (the one with pseudo low level code) an optimizer could do such silly thing and break working code. ... – ShinTakezou Apr 23 '11 at 14:08
  • Another interesting fact, I've just installed splint; running on the code, it says: Argument 2 modifies i, used by argument 3 (order of evaluation of actual parameters is undefined): printf("%ld\n", ++i, i + 1) Code has unspecified behavior. Order of evaluation of function parameters or subexpressions is not defined, so if a value is used and modified in different places not separated by a sequence point constraining evaluation order, then the result of the expression is unspecified. .. It seems like splint's programmers haven't got it too. – ShinTakezou Apr 23 '11 at 14:12
  • @ShinTakezou; If the compiler decided to inline the functions and lost the sequence point in the process, then that compiler is hopelessly broken. The compiler can inline anything it wants, but it is not allowed to change the behavior of the code. Code with defined behavior cannot become undefined because of inlining (again, unless the compiler is broken). – AnT stands with Russia Apr 24 '11 at 05:40
  • @ShinTakezou: As for Splint, it really depends how pedantic these programmers wanted to get. Calling this behavior *unspecified* instead of *undefined* is an error from the formal and pedantic point of view. – AnT stands with Russia Apr 24 '11 at 05:42
  • @AnT This is the answer I was really searching for on the Internet. Thank you so much from 2022! – JenyaKh Jan 17 '22 at 18:26
2

Most students said undefined behavior. Can anyone help me understand why it is so?

Because order in which function parameters are calculated is not specified.

Vladimir
  • 170,431
  • 36
  • 387
  • 313
  • 1
    but the order is irrelevant because only one parameter is being printf'd – KevinDTimm Aug 10 '10 at 15:31
  • 5
    That's irrelevant; the fact that there is a `++x` and `x+1` without an intervening sequence point is what makes it undefined. – David Thornley Aug 10 '10 at 16:04
  • those std definitions compliances make me always (m/s)ad. Since printf will use, driven by "%d", only the first arg passed, which is btw the only one that modifies x causing side effects. Since x+1 does not change x, it can be evaluated before, or later, or never, and the result of ++x won't change; and since ++x is the only one taken, the result/behaviour is not undefined, unspecified or any other std-word. It is always that, and no matter the implementation; printf is (fmt, ...) and though compilers can check if the fmt matches the extra args, this is not mandatory, nor always desired – ShinTakezou Aug 10 '10 at 16:32
  • 1
    @ShinTakezou: If you don't like paying attention to the Standard, that's one thing, but you really shouldn't get into discussions like this if you don't. Moreover, if you combine that attitude with a willingness to use non-conforming code, sometime you're going to get bitten by an assumption you made, and the compiler writers are going to have no sympathy. – David Thornley Aug 10 '10 at 16:37
  • I am saying that interpreting standard in these cases is like interpreting law; there are lawyers that can make the same thing once good for the law, and once bad. Printf is a vararg func, so three args are ok. The result of one (x+1) is undefined because of the side effect of the eval of the other (++x) and the fact that std does not dictate an order of evaluation. But printf, because of %d, will actually take only the result of the evaluation of ++x, which will be __always__ 2, since x+1 has no side effects. So the result will be __always__ 2. – ShinTakezou Aug 10 '10 at 16:48
  • 1
    @ShinTakezou: And I am saying that this is undefined behavior, according to the letter of the standard, and nobody familiar with the standard will insist on telling you anything else. There are legitimate arguments about various standards, and this is not one of them. The fact that printf() will use only one value is irrelevant here as far as the standard is concerned. The fact that one thing is actually undefined, even if it isn't used, means the whole thing is undefined. I would be very surprised to find an implementation that didn't output 2, but it would be within its rights. – David Thornley Aug 10 '10 at 16:59
  • @David prolog: no-one would consider good code `printf("%d", x+1, x+1)` though "defined behaviour" (or printf descr in the std does not say "extra" args, with respect to fmt, are ignored?).Let's take a vararg func like xyz.If you're asked about `xyz(f, ++x, x+1)`'s output, you must say undef. behaviour, by std... in this case you mean you can't be able to predict output, since you don't know what xyz does. But here the Q is simply about printf and what is its output __always__. The correct answer is 2, ragardless of what the std say. There can't be any C impl that produces different output – ShinTakezou Aug 10 '10 at 17:14
  • @ShinTakezou: There certainly can be a conforming C implementation that produces different output. How likely that is is another question. If you really want me to, I'll write a "compiler" that scans for those exact characters and prints "42" if it finds them, otherwise passing it off to a real compiler. Assuming the real compiler is conforming, mine is conforming. – David Thornley Aug 10 '10 at 17:18
  • @David do it please; and of course, it must be able to compile also the "defined behaviour" code. I say that a correct C compiler (according to the same std that labels this undef behaviour) can't output something different from 2. – ShinTakezou Aug 10 '10 at 17:21
  • @ShinTakezou: A C compiler that's correct according to the standard can do anything it likes on encountering any undefined behavior. I can write a Perl script that takes a program, scans it for those characters, and if it doesn't find them passes it to a real C compiler. That means that it properly compiles all conforming code, since that code isn't conforming. You will not find anything against it in the standard. I will freely admit that it's a pointless implementation for most purposes, but it is conforming. It is correct according to that standard. – David Thornley Aug 10 '10 at 17:29
  • @David oh sorry missed the "build a compiler that forces wrong results on code that is defined undef behaviour from the std". I could write code that your compiler thinks is undefined behaviour according to the std, but it indeed is not... and this would make your compiler not standard compliant. – ShinTakezou Aug 10 '10 at 17:32
  • @ShinTakezou: David said his script scans for instances of UB, you're argument is that his script is sometimes wrong but the basis of his argument was a correct script. You can't construct a valid counter-argument from this position, it's illogical. – CB Bailey Aug 10 '10 at 17:43
  • @Charles Bailey have you ever read Godel Escher Bach? There's always an escape:D You would be right, if you think it can exist an implementation of such a script able to catch __all__ the "undefined behaviour" code, digest it someway, and then pass it to a real compiler. It is __not__ illogical to think that may exist a code, designed to mislead David's script, so that it makes "undefined" a code that is perfectly defined, so demonstrating that the script is not a standard-compliant compiler. I've already written it in the prev comment, with less words. – ShinTakezou Aug 10 '10 at 18:02
  • @ShinTakezou: How can you mislead his script? You can only do this if the script is incorrect. In fact, for David's argument to hold it's not necessary for the script to diagnose all UB (probably impossible as some UB depends on program input); just UB of this form or even just this particular construct would be sufficient. – CB Bailey Aug 10 '10 at 18:20
  • @Charles: Right. All it has to do is scan for `printf("%d",++x,x+1); `. I can't write a general UB detector, that's impossible (some UB detection is equivalent to the halting problem), but that particular statement is legally UB. Therefore, it processes some UB in a silly way, and passes all else, including lots of UB, to a real compiler. It's conforming, if the real compiler is. – David Thornley Aug 10 '10 at 19:23
  • @Charles, help him writing the script, send it to me, and I will try to mislead it. I don't know if it would prove anything, but it would be funny. Scanning for that particular line would change only that particular code! You have to pick the standard, and write a parser to parse at least that single case that makes that particular code UB: you have to catch the "feature" that makes it UB, not the exact code! It could be mislead anyway too - for example through macros – ShinTakezou Aug 10 '10 at 20:42
  • @ShinTakezou: Of **COURSE** it only changes that particular code. I can't possibly detect all UB, and for this purpose it isn't worth my while to do parsing or any further analysis. If I read 7.1.3 of the draft C99 standard aright, you can't `#define printf(` legally, so I don't have to worry about that. – David Thornley Aug 10 '10 at 21:39
  • @David your only escape would be to pre-pass the code to cpp, or rewrite a cpp in your script, otherwise you'll never be able to catch that UDB;I don't need to define printf(, a lot of "dirty" tricks are possibles and make your work impossible (unless you use cpp or rewrite cpp as already said) - btw vladimir's answer is wrong since it talks about USB; if this would be only USB depending on order of eval, then my answer would be the right one (changed all UDB into USB and removed the "mixing"); if this would be just USB, the answer would be 2, w/o further possible discussion. – ShinTakezou Aug 12 '10 at 14:42
  • @ShinTakezou: Wrong. All I have to know is that `printf("%d",++x,x+1);` is UB. Therefore, I can do a simple textual scan for that. I'll miss a whole lot of UB, but that doesn't matter. And, yes, if this was USB the answer would be 2. – David Thornley Aug 12 '10 at 14:57
  • @David Wrong? Seriously?! Which part? You won't find that string to scan for, if I use macros! And if you preparse it through cpp, ... I've already said it's your only escape... So, which part is wrong?! Moreover you can't believe it is so simple - it could be simple, but not that simple: I could write x=x+1 instead of ++x, the "UDB" is __exactly__ the same, but you've to add code to your script. Textual scan of a complex syntax is not that easy; beyond the x=x+1 trick, what about comments? you must skip them... taken already into account? it seems not>> – ShinTakezou Aug 13 '10 at 08:09
  • @David moreover, reading [this](http://en.wikipedia.org/wiki/Sequence_point) I start to argue that the output is always 2, even if it is UDB - if you want, you can help another user to write a compiler to demonstrate it is not so (he wanted 3 years and a grant - or maybe was you? I can't remember names and too lazy to check), and I'll take 3 years to check your compiler carefully - I bet it won't sell too much... - since a compiler that refuses UDB and USB for sure, but may fail compiling legal code, is not so useful. – ShinTakezou Aug 13 '10 at 08:12
2

What output will it always produce ?

It will produce 2 in all environments I can think of. Strict interpretation of the C99 standard however renders the behaviour undefined because the accesses to x do not meet the requirements that exist between sequence points.

Most students said undefined behavior. Can anyone help me understand why it is so?

I will now address the second question which I understand as "Why do most of the students of my class say that the shown code constitutes undefined behaviour?" and I think no other poster has answered so far. One part of the students will have remembered examples of undefined value of expressions like

f(++i,i)

The code you give fits this pattern but the students erroneously think that the behaviour is defined anyway because printf ignores the last parameter. This nuance confuses many students. Another part of the student will be as well versed in standard as David Thornley and say "undefined behaviour" for the correct reasons explained above.

Peter G.
  • 14,786
  • 7
  • 57
  • 75
  • 1
    The behavior is undefined because `x` is assigned to, and referenced in a different context, within the same sequence points. The output will almost always be 2. There is no requirement in the C standard that makes that so. – David Thornley Aug 10 '10 at 16:19
  • @David I checked see standard and technically you are 100 % right. I will correct my answer now. – Peter G. Aug 10 '10 at 16:41
  • 1
    @Peter G.: "...but the behaviour is defined anyway...". How does it become "defined anyway"? Undefined behavior in this case is not in any way "attached" to the last parameter of `printf`. Just because `printf` ignores it does not suddenly make the behavior defined. – AnT stands with Russia Aug 10 '10 at 16:55
  • @Andrey Thanks. In the revised answer I meant to say the students will be believe it to be defined anyway (like I did not long ago ...). I corrected that now in the revised^2 answer. – Peter G. Aug 10 '10 at 17:24
  • I need to stress that my +1 was for "The code you give fits this pattern but the behaviour is defined anyway because printf ignores the last parameter". Even though, for std-lawyer, you should stress the fact that you are using "defined behaviour" in the common sense, not as for std-definition of what is not undefined-behaviour. A program that, given an input, emits always the same output, so being predictable, shouldn't be defined "undefined behaviour", even if formally it could be. – ShinTakezou Aug 10 '10 at 17:28
  • @ShinTakezou: Except that, without detailed knowledge of every C implementation that purports to be conforming, it's not possible to know that that program will always have the same output. – David Thornley Aug 10 '10 at 19:25
  • @David you don't need detailed knowledge of every C impl to say that, exactly how you don't need that knowledge to argue that `printf(x+y)` will output x+y even on non standard compliant impl of C. – ShinTakezou Aug 10 '10 at 19:36
  • @ShinTakezou: Okay, what are you reasoning from? It sure isn't the standard, and you've admitted it isn't from detailed knowledge of every C implementation there ever was. Statements of universals are generally based on fundamental principles or exhaustive knowledge, and preferably not on oft-repeated but unsupported assertions. Heck, you haven't shown any problem with Gilles' answer; there's no reason why code generation couldn't do exactly that. – David Thornley Aug 10 '10 at 19:43
  • @David from how people creates parsers and compilers and how what you write becomes low level code. I've shown problem with Gilles' answer indeed, but how depends on the focus he gave his answer and details he gives. There are comments I've not replied yet, but here it's getting dark and I am finishing my ability to write coherently in my fluent english (irony of course) – ShinTakezou Aug 10 '10 at 19:59
  • @ShinTakezou: No, you said that if that generated code was used in another context it would be inappropriate. That doesn't say there was anything wrong with Gilles's answer. When you come back, please provide some argument that does not imply omniscience on your part. – David Thornley Aug 10 '10 at 21:27
  • @David who assure you I am not omniscent? You should be omniscent, to say I am not. I've said the code would make a thing like (x+1, x+1) fails. Don't forget the code is generated "automatically", not by a human; we have to imitate the process, without adding human ability to cheat. If the process follows the same path (I expect this), it would use the register as the original value also in the shown case (x+1,x+1). The context is not different: there are just different exprs. If that path is an exception for the presence of ++x, then why the presence of a ++x should trigger that exception?... – ShinTakezou Aug 12 '10 at 14:52
  • ... I can't see a reason. The most "stupid" "generator" would translate ++x into, say, "mv (x), r0; add #1, r0; mv r0, (x); push (x)", and x+1 into, say, "mv (x), r0; add #1, r0; push r0"; in both cases r0 is a scratch register and any optimization must not mix stuffs, the operation ++x in this is not special. In a generator that "looks" at all exprs "in a glance", the question is just when x is updated, and when and if it is picked back; but the generator must track changes to r0 (or whatever), if it is an alias, or as said wrong code can be generated even for legal code; ... – ShinTakezou Aug 12 '10 at 15:01
  • @ShinTakezou: That code would only be generated if there was an assignment, and f(x+1,x+1) does not contain an assignment. Automatic code generation is pretty smart nowadays (actually, it's been pretty smart through the history of compiling, since compilers couldn't have become popular without at least being close to human code generation in efficiency). I cannot understand your last sentence. If the path is an exception in case of assignment, then of *course* an assignment would trigger that exception. Why else would it be there? – David Thornley Aug 12 '10 at 15:02
  • ... say it differently: interpret ++x as "x+1 and update it". To update x to x+1, of course x+1 must be computed first. The only added part is the updated process. But being this present or not, the next expression can't reuse the already computed x+1 to calculated "its" x+1, that would then become (x+1)+1. So we just have a "vagabond" update instruction, that will be executed for sure before the end of the statement, but we just don't know when. And this is why, explained this way, it is clear the UnSpecified Behaviour. Recovering the UDB, ignored not only by me, ...I have no explanation... . – ShinTakezou Aug 12 '10 at 15:06
  • @David why should the code be UDF in case there's an assignment, being the assigment just as saying: compute the expression, update the in memory value...? I understand the USB, still refuse the UDB since also the parallel argument does not convince me. Btw printf("%d %d", y = x+1, x+1) is ok and of course is defined and specified behaviour, printing 2 2 for sure. There's an assignment, though to another variable. If this is legal code, it means the problem is not the assignment, but to what it is assigned the result of the expression, i.e. the "update the same memory cell" stuff – ShinTakezou Aug 12 '10 at 15:12
  • @ShinTakezou: Okay, an assignment to x triggers that behavior for x. I have no idea why you refuse to call it undefined behavior, since that's exactly what it is. I have no idea why you insist that all compilers must work the way you imagine them working, particularly since you seem to have an inadequate grounding in code generation (so do I, in this case, but I don't assume I know how it has to work). I have, in a comment to another answer, pointed you towards documents you can study to understand why the Standard is what it is. – David Thornley Aug 12 '10 at 15:25
  • @David I've seen them and thanks.If you've no ground in code generation,how can you judge mine?Are you omniscent too?I refuse it since it's a reasonless definition. Rules exist not just for fun (since I expect std writers had something in mind!);there must be one, and noone is able to give any (parallel argument apart, but this explains easily USB, while to explain UDB someone expert in parallelism should explain how that 2stuffs "interact"). Now the"OP" can do 2 things: prove he's read the std and can "apply" it blindly, or prove he's able to think and have lots of question about. – ShinTakezou Aug 12 '10 at 15:37
1

The points made about undefined behavior are correct, but there is one additional wrinkle: printf may fail. It's doing file IO; there are any number of reasons it could fail, and it's impossible to eliminate them without knowing the complete program and the context in which it will be executed.

Thom Smith
  • 13,916
  • 6
  • 45
  • 91
0

Echoing codaddict the answer is 2.

printf will be called with argument 2 and it will print it.

If this code is put in a context like:

void do_something()
{
    int x=1;
    printf("%d",++x,x+1);
}

Then the behaviour of that function is completely and unambiguously defined. I'm not of course arguing that this is good or correct or that the value of x is determinable afterwards.

Daniel
  • 1,994
  • 15
  • 36
  • I agree with you and codaddict saying the output will be 2 all the time; but there is a lot you can read about it, there are people believing this is undefined behaviour and thus we can't say it is always 2... I am trying to understand if their claim is reasonable; currently, I can't see good explanations to say they are right and we are wrong (i.e. that we can't have arguments to say that it is always 2); if you have time, patience, and you want, you could try to give more solid arguments for your answer. – ShinTakezou Aug 13 '10 at 08:01
-1

The output will be always (for 99.98% of the most important stadard compliant compilers and systems) 2.

According to the standard, this seems to be, by definition, "undefined behaviour", a definition/answer that is self-justifying and that says nothing about what actually can happen, and especially why.

The utility splint (which is not a std compliance checking tool), and so splint's programmers, consider this as "unspecified behaviour". This means, basically, that the evaluation of (x+1) can give 1+1 or 2+1, depending on when the update of x is actually done. Since however the expression is discarded (printf format reads 1 argument), the output is unaffected, and we can still say it is 2.

undefined.c:7:20: Argument 2 modifies x, used by argument 3 (order of evaluation of actual parameters is undefined): printf("%d\n", ++x, x + 1) Code has unspecified behavior. Order of evaluation of function parameters or subexpressions is not defined, so if a value is used and modified in different places not separated by a sequence point constraining evaluation order, then the result of the expression is unspecified.

As said before, the unspecified behaviour affect just the evaluation of (x+1), not the whole statement or other expressions of it. So in the case of "unspecified behaviour" we can say that the output is 2, and nobody could object.

But this is not unspecified behaviour, it seems to be "undefined behaviour". And the "undefined behaviour" seems to have to be something that affect the whole statement instead of the single expression. This is due to the mistery around where the "undefined behaviour" actually occur (i.e. what exactly affects).

If there would be motivations to attach the "undefined behaviour" just to the (x+1) expression, as in the "unspecified behaviour" case, then we still could say that the output is always (100%) 2. Attaching the "undefined behaviour" just to (x+1) means that we are not able to say if it is 1+1 or 2+1; it is just "anything". But again, that "anything" is dropped because of the printf, and this means that the answer would be "always (100%) 2".

Instead, because of misterious asymmetries, the "undefined behaviour" can't be attached just to the x+1, but indeed it must affect at least the ++x (which by the way is the responsible for the undefined behaviour), if not the whole statement. If it infects just the ++x expression, the output is a "undefined value", i.e. any integer, e.g. -5847834 or 9032. If it infects the whole statement, then you could see gargabe in your console output, likely you could have to stop the program with ctrl-c, possibly before it starts to choke your cpu.

According to an urban legend, the "undefined behaviour" infects not only the whole program, but also your computer and the laws of physics, so that misterious creatures can be created by your program and fly away or eat you.

No answers explain anything competently about the topic. They are just a "oh see the standard says this" (and it is just an interpretation, as usual!). So at least you have learned that "standards exist", and they make arid the educational questions (since of course, don't forget that your code is wrong, regardless undefined/unspecified behaviourism and other standard facts), unuseful the logic arguments and aimless the deep investigations and understanding.

ShinTakezou
  • 9,432
  • 1
  • 29
  • 39
  • @ShinTakezou: the reason why the standard writers decided not to define the behavior of `f(++x,x+1)` is that they chose to follow a simple principle: during a stretch of parallel execution (i.e., between two sequence points), each variable may have either one write or any number of reads, but not both. This is a common design principle in concurrent or parallel systems. – Gilles 'SO- stop being evil' Aug 10 '10 at 22:26
  • @ShinTakezou: by the way, what behavior do you expect from `printf("%d %d", ++x, x+1)` and from `printf("%d %d", x+1, ++x)`? – Gilles 'SO- stop being evil' Aug 10 '10 at 22:27
  • @Gilles I read this after writing the comment about non-existance of an expl. for the choice; but it is not so much clear; I would say USB as "func1(&x), func2(x)", unless "++x, x+1" can be parallelized while "func1(&x), func2(x)" would not (why?);the problem in the parallelization isn't in the side-effects, that are both present in ++x, x+1 and func1(&x), func2(x) ?; even with parallelization in mind, I can't see any reason to make it UDB instead of USB as caf said for func1(&x), func2(x); example of non bogus parallelization that justifies that instead of USB would be appreciated – ShinTakezou Aug 12 '10 at 06:48
  • the result from both your code with both %d is unpredictable, since it could be (2, 3), (2, 2), and (2, 2) or (2, 3) according to the order of evaluation of the exprs. I suppose this comment should have been obvious from the answer itself. Since it is unpredictable (i.e. compiled on different compilers can give different results and without trying we can't say which one), it is something to avoid and a clue for wrong programming, but this is not interesting in a educational context. – ShinTakezou Aug 12 '10 at 06:56
  • 1
    *The short answer is no, since doing so the compiler could break its ability to compile correctly some "legal" code...* If the compiler found two references to the same `int` variable in the argument list to a function call, and one of them used `++` on the variable, it could emit the code to print an error message about undefined behaviour and then call `abort`. It would be perfectly standard conforming, it would still compile all standard-compliant programs correctly, but it would not output `2` from the program given by the OP. Is that any clearer? – Daniel Earwicker Aug 12 '10 at 12:57
  • You make reference to the order of operations, but in C they're as little specified as possible, and of course there are no requirements for undefined behavior. You are being very dogmatic on limitations of compilers, which are of course free to special-case whatever they like. Also, "sequence point" is defined by the Standard, and "++x" does not cause one. – David Thornley Aug 12 '10 at 15:31
  • @Daniel it's clear and __totally unuseful__ . Making blind agreement to the standard by adding code is silly(1). As said elsewhere, the definition that says it is UDB must be a consequence of choices, that must have"solid" reason(s), that surely is "felt" compilers writers, but here many people insist just on "respect the std w/o questions nor reasoning and you'll be happy".If std would've been written by stupid,it would be the only option. (1) UDB should appear "naturally" as conseguence of something else. I want someone to write a possible "something else", not the banal fact that >> – ShinTakezou Aug 12 '10 at 15:48
  • >> not the banal fact that a compiler written to be perfectly standard compliant, will be perfectly std compliant. – ShinTakezou Aug 12 '10 at 15:50
  • @David I suspected all defs are in the std. But what's the point of SO if an OP have to dig into the std while there are people familiar with it that could have copy-pasted them altogether with the part explaining the UDB? - Where am I "dogmatic"? Std is heavely dogmatic and no problem with that - And I believed programmers disliked special cases, since they force the writing of more code ;; I've taken into account unspecification of order of exec (indeed the only thing I've taken into account), but it causes USB, not UDB;in fact in this ans I mix them (I'll fix once I get why it's UDB) – ShinTakezou Aug 12 '10 at 15:57
  • (I'll fix once I get why it's UDB not dogmatically, I mean, once I have a clear explanation of why it is not simply USB... the explanation could be also it was a mistake that will be fixed in the next standard, of course... then I have to find the time to partecipate in the standard writing to avoid such monstruosities:D) – ShinTakezou Aug 12 '10 at 15:58
  • @ShinTakezou - It depends. Either you're talking about what is *likely to be true* (in which case, as the accepted answer says, "The output is likely to be 2 in every reasonable case"), or you're talking about what can be *definitely asserted* if all you know is that the compiler is standard-compliant (and if the compiler is designed to help catch undefined behaviour, it would be very useful for it to print an error message.) Or you're talking about what the next version of the standard should say, which is yet another completely different issue. – Daniel Earwicker Aug 12 '10 at 16:39
  • @Daniel all 3 things. The last, if the "others" are totally right, then the std should be fixed - self-justified existance of rules shows weakness, imo. If they are not right (it is not clear at all, even though you can think it is),the ans is "it is always 2". If compliant compilers are built so that by default they don't stop on UDB, it means they are able to produce a code; once that code is "produced", running it will give always 2 (this is my claim);if the compiler won't compile,the OP's teacher should pick another example to show the point(I daresay it is not teaching UDB...) – ShinTakezou Aug 13 '10 at 07:14
  • 1
    @ShinTakezou - *once that code is "produced", running it will give always 2 (this is my claim)* — it is perfectly possible for a standards compliant C implementation to produce code for that expression that does not result in "2" for the second argument. Whether you know of one that does this or not is irrelevant, the question is whether it is possible, and it is. – detly Aug 13 '10 at 08:36
  • @ShinTakezou - "running it will give always 2". I'm now convinced that you use the word "always" in a sufficiently unusual sense as to make further discussion unproductive. – Daniel Earwicker Aug 13 '10 at 08:41
  • @Daniel Earwicker ... what?! I am using the word "always" exactly as a way of saying "likely" where likely is around 99.98%. I've tried gcc and g++ (yes) on GNU/Linux and MS Windows; microsoft compiler on MS Windows; sun studio compiler cc/CC (C++); I've tried clang (on GNU/Linux); I miss the intel C compiler, I bet it outputs 2; I've tried also the HP compiler; do you know any other C compiler worth noting? Try them; when you've finished, you are at 99.98%, the best answer "likely", my "always". @detly the question of the OP is about what actually is the output; then, we can discuss stds – ShinTakezou Apr 23 '11 at 14:39
  • moreover I've just installed splint, and going to modify the answer to be shorter and more usable – ShinTakezou Apr 23 '11 at 14:40
  • @DanielEarwicker: If a compiler produces machine code which loads x with 1, increments it, pushes that value, increments it again, and pushes that value, then running that *generated machine code* will always yield the same result. The Standard would not forbid a compiler from generating code which e.g. checks whether today is Friday and prints 3 if so, or 2 otherwise, but for many forms of UB compilers will usually generate machine code which is 100% deterministic, even though they are not required to do so. What's uncertain is when compilers will or will not generate such code. – supercat Apr 12 '16 at 18:10