6

During my preparation to exam on ANSI C I have encountered the following question -

Is following statement valid?
If not, please make required changes to make it valid.

The original statement is: test(i++,i++); it isn't valid because the behaviour is undefined according to K&R p202

The order of evaluation of arguments is unspecified

But can I change it to the following statement? test(i+=2, i+=3)?

The problem that I haven't seen such record in K&R or any other source. But XCode compile it and run without any warning.

Anatoly
  • 5,056
  • 9
  • 62
  • 136

5 Answers5

12

Both are valid statements, i.e. legal C, and in both cases, the behavior is undefined.

Jack Whitham
  • 609
  • 4
  • 9
  • 6
    It depends on what you mean by "valid". Plenty of people will claim that a program that has undefined behaviour is not "valid", and those claims are at least just as legitimate as yours. –  May 08 '15 at 10:32
  • If you say it that way, What is invalid then? – dhein May 08 '15 at 10:32
  • @hvd sir, I think by saying _valid_ OP meant _valid to compiler_. – Sourav Ghosh May 08 '15 at 10:33
  • aaa.... I got it, actually I didn't change anything by rewriting my statement and need to create additional variables which will calculated prior to function call. Thank you. – Anatoly May 08 '15 at 10:34
  • 3
    Do not confuse _valid_ (as in, syntactically) and _correct_ (or "strictly conforming" according to The Standard) programs. – Michael Foukarakis May 08 '15 at 10:35
  • There are plenty of instances of undefined behavior that are nevertheless valid, i.e. accepted by the C standard and the C compiler. C programmers use some of them all the time - consider, for instance, how floating point numbers are rounded. Obviously it is not good practice to write `test(x++,x++)` but a C compiler can only generate a warning if you do. – Jack Whitham May 08 '15 at 10:37
  • 1
    @JackWhitham: That is patently false. Not only can compilers do whatever they want with undefined behaviour, it is a fact they will assume a program contains no UB in order to perform optimisations. – Michael Foukarakis May 08 '15 at 10:42
  • @Jack Whitham: It is arguable that a compiler that would issue errors instead of warning on statements proven to contain invalid behaviour would just exhibit some interesting and useful kind of undefined behaviour. – chqrlie May 08 '15 at 10:43
  • @MichaelFoukarakis Which part is "patently false"? Programs often involve statements and expressions where the behavior is not precisely defined by the C standard. I even gave an example of this, namely handling of floating point numbers. Are you really claiming that the C standard *precisely specifies* what floating point operations should do? If not, then surely you would have to concede that reliance on undefined behavior is actually pretty common. – Jack Whitham May 08 '15 at 11:35
  • @JackWhitham: More to the point, there are plenty of cases where a behavior is left undefined by the C standard, but an implementation promises a particular behavior. For example, the C standard does not say whether -1<<2 should yield -4 or whether it should negate the laws of causality, but many implementations document that left-shifts of negative numbers behave as multiplication, and it is perfectly legitimate to use left-shift in such fashion on such implementations. – supercat May 08 '15 at 13:55
  • @supercat you're confusing *undefined* with *unspecified* there. – Antti Haapala -- Слава Україні May 10 '15 at 04:32
  • Since this program has undefined behaviour, it does not even have to *compile* - that is, there is this class of errors called *undefined behaviour*, some conforming compilers on some inputs do not compile at all for some of these class of errors. – Antti Haapala -- Слава Україні May 10 '15 at 04:35
  • @AnttiHaapala: If `x` and `y` are pointers to distinct objects, `x=haystack && needle < hastack+size` will imply that the needle is within haystack, were expected to do so; those which couldn't... – supercat May 10 '15 at 22:50
  • ...wouldn't be able to run certain kinds of code effectively, *but could nonetheless allow the C programming language to be used for other tasks not requiring such ability*. BTW, my guess as to why the `<` yields undefined rather than unspecified behavior is to allow for the possibility that a system where making `<` yield consistent results on unrelated pointers would be expensive might provide three options: (1) have such comparisons generate slow code which yields a consistent ranking; (2) have such comparisons yield an inconsistent ranking; (3) have such comparisons trap, at least in... – supercat May 10 '15 at 22:57
  • ...testing builds, when trying to track down why e.g. code which is supposed to sort pointers is leaving them unsorted [many sort algorithms will malfunction badly if `x>y` and `y>z` doesn't imply `x>z`]. From a practical standpoint, having comparisons of arbitrary pointers yield unspecified results isn't much more useful than Undefined Behavior, and traping such comparisons may be more useful than yielding bogus results *on systems which can't yield good results*. – supercat May 10 '15 at 23:02
12

To add up to the existing answers, the key point is, the statement

test(i+=2, i+=3)

invokes undefined behavior as much as

test(i++,i++);

because, in both the cases, there is no sequence point scheduled for comma separator in function parameter list and hence, you end up modifying the value of the same variable twice in the scope of a single sequence point. This invokes undefined behaviour.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
7

Statements are syntactically valid, both before and after the change. But still the problem will remain. If you are modifying an object in the argument, and the order of evaluation is unspecified.

C99 Section 6.5.2.2 Paragraph 10

The order of evaluation of the function designator,the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.

As per Section 3.4.4 Paragraph 1

unspecified behaviour

use of an unspecified value, or other behavior where this International Standard provides two or more possibilities and imposes no further requirements on which is chosen in any instance.

On the other hand Section 3.4.3 Paragraph 1 tells

undefined behaviour

behavior,upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirement

In the case of the order or evaluation, it can be done in any order, depending on how the compiler generates the code, it might store in-memory in any order and also may pass the arguments through register. Once the code is generated, the binary will behave same everywhere. Therefore for one single binary the results will be identical every time, but depending on the decision of the compiler things can change.

The best idea is to avoid anything which seems to be incorrect or fancy. When in doubt, possibly it is an undefined, unspecified, implementation-defined behaviour. Therefore you can make the same thing unambiguous and deterministic as follows.

test (i, i+1);
i += 2;

OR

test (i+1, i);
i+= 2;

Depending on what order you want.

phoxis
  • 60,131
  • 14
  • 81
  • 117
  • Interesting! The order is not actually undefined, but merely unspecified. This is different. But there is no sequence point between the evaluation of the arguments, so the behaviour is still undefined. – chqrlie May 08 '15 at 10:52
  • 1
    What makes it undefined behaviour is the multiple side effect on the same variable `i` between 2 sequence points. unspecified behaviour would be this: `test(printf("arg1\n"), printf("arg2\n"));` – chqrlie May 08 '15 at 10:58
  • @chqrlie Iam not able to get it. Can you clarify on the other side effect which makes the behaviour undefined instead of unspecified? – phoxis May 08 '15 at 11:01
  • Sourav Gosh's answer is simple and clear: modifying the *same* variable more than once without an intervening sequence point invokes undefined behaviour, There is no sequence point between `i++` and `i++` in the OP's code fragment. In me example, there is one upon entry of the `printf()`, so even if `printf` modifies the same variables (it does!), the order of execution of both calls is unspecified but not undefined. – chqrlie May 08 '15 at 11:07
  • Therefore it should be the effect of both actually. – phoxis May 08 '15 at 11:09
  • No: you will either get `arg1\narg2\n` or `arg2\narg1\n` on `stdout` before whatever `test()` does. – chqrlie May 08 '15 at 11:14
  • Not on the function example you gave, for that I can understand. For the original question, yes there is no sequence points between the two arguments, also as quoted the behaviour is unspecified. Therefore the behaviour is undefined as a whole. I wanted an opinion on this. – phoxis May 08 '15 at 11:17
  • unspecified order means *the compiler may chose one or the other*. undefined behaviour means *anything can happen*. – chqrlie May 08 '15 at 11:31
  • That is correct, my point is, in this case if this is undefined it or unspecified. – phoxis May 08 '15 at 11:35
  • 1
    `test(i++, i++)` invokes undefined behaviour, whereas `test(a(), b())` just has unspecified behaviour. – chqrlie May 08 '15 at 11:37
4

This sentence is quite confusing: test(i++,i++); it isn't valid because the behaviour is undefined according to K&R p202 .

The truth is this statement has always been invalid in C. From the original specification of C by Kernighan and Ritchie in their book The C programming language to the latest C11 Standard published a few years ago, including the older C99 Standard and the obsolete C89 standard also known as ANSI C. Just not for the reason stated.

The order in which the aguments to the function test are evaluated is unspecified, but this is not the problem here: both expressions modify the same variable and there is no sequence point between function argument evaluation. So it does not matter how you achieve side effects in the expressions used for the arguments, you invoke undefined behaviour. The compiler may generate code, but the rocket may explode upon take-off.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Order of evaluation is [unspecified not undefined](http://stackoverflow.com/a/24924172/1708801), it is multiple modification within a sequence point that is undefined. – Shafik Yaghmour May 08 '15 at 13:20
4

As others have already noted, the behaviour in both cases is undefined, even if the code is syntactically valid in both cases. I assume the question uses "valid" to mean "correct", as in a strictly conforming C program. To make the statement correct, you must first know/derive its intent.

That could in fact be impossible without some external source telling you exactly what that intent is, but let's assume for the sake of argument that the programmer wishes to invoke the function test with the parameters (i+1, i+2) (in order of appearance). It would be best to communicate this intent by simply:

test (i + 1, i + 2);
i += 2;

avoiding any ill effects introduced by the unspecified order of evaluation of function arguments.

Michael Foukarakis
  • 39,737
  • 6
  • 87
  • 123