문제

I am reading about UB from 2 days and a confusion is growing in my mind about the following example

int a=5;
a++ & printf("%d",a);//i know that `&` introduced here is not a sequence point.

Now,it is UB in c but i am looking at it from a different angel as following

now c 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.

So,as we are modifying the value of a by writing a++ only once,this rule fails in declaring the behavior as UB.
But further it states that

Furthermore, the prior value shall be accessed only to determine the value to be stored.

As the prior value of a ( 5 here ) is accessed via a++ to write the value of a .But in the statement printf("%d",a)

("%d",a) is a full expression.so all the dust of side effect should be settled down before the expression runs.
As according to standard

when a function call is made there is sequence point before the actual call and after the evaluation of arguments.

so a should have been modified here in the function call,more precise to say that value of a is updated according to the standard.
Then why it is yet UB as ("%d",a) is an expression in itself and it is evaluated before ;?

도움이 되었습니까?

해결책 2

The expression a++ & printf("%d",a) contains five interesting steps:

  • A) read a
  • B) increment a
  • C) read a
  • D) call printf
  • E) bitwise-and

You may expect C++ to execute these steps from top to bottom (A->B->C->D->E), because humans tend to read from left to right. But in fact, C++ only gives you the following guarantees:

  • A is executed before B (A->B)
  • C is executed before D (C->D), because functions need to know their arguments
  • A and D are executed before E (A->E, D->E), because operators need to know their operands
  • If A is executed before D (A->D), then B is also executed before D (B->D), because a++ has a side effect, function calls are sequence points, and side effects are guaranteed to be visible by then.

Note that there is no guarantee of the form (A->C) or (B->C). This is what most beginners don't get.

Does C read the value before or after the increment that happens at B? We do not know!

  • The operator & does not impose any restrictions on the order of evaluation of its operands, so the second operand printf("%d",a) may be evaluated before the first operand a++. (The evaluations of the operands may also be interleaved, as long as they obey the above guarantees.)
  • Even if the first operand is evaluated before the second operand, the side effect that happens in B is not guaranteed to be visible when C is executed. (It would be visible at D then, because function calls are sequence points, but by then it's too late. The arguments have already been evaluated.)

These restrictions leave us with the following possible execution orders:

  • A->B->C->D->E
  • A->C->B->D->E
  • C->A->B->D->E
  • C->D->A->B->E
  • C->A->B->D->E

It took me several attempts to write this list down. I am not 100% certain it is correct and complete.

다른 팁

("%d",a) is not a full-expression. In this context, it is not even an expression. It is the list of arguments to a function. The full-expression in your code is everything on your second line, except for the ;.

A full expression is an expression that is not part of another expression or of a declarator.

The access of a within the argument list does not have an intervening sequence-point between it and the a++, and it is not for the purpose of doing a++, so the behaviour is undefined.

So,as we are modifying the value of a by writinga++only once,this rule fails in declaring the behavior as UB.

Here a++ & printf("%d",a); - the only sequence points are ; and before printf is entered when invoked.

You can clearly see a is being modified and being read from at the same time within a single sequence point ;. Hence re-stating with emphasis:

A sequence point is a point in time at which the dust has settled and all side effects which have been seen so far are guaranteed to be complete. The sequence points listed in the C standard are:

at the end of the evaluation of a full expression (a full expression is an expression statement, or any other expression which is not a subexpression within any larger expression); at the ||, &&, ?:, and comma operators; and at a function call (after the evaluation of all the arguments, and just before the actual call).

The Standard states that

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 accessed only to determine the value to be stored.

What you are confusing is about what side effects are. Both printf("%d",a) and a++ have side effect, according to:

C11 5.1.2.3 Program execution

Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects,12) which are changes in the state of the execution environment.

printf("%d",a) is modifying the state of the execution environment. A function that has no side effect is a function that does nothing except return a value that is determined only by its arguments.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top