Could anyone explain these undefined behaviors (i = i++ + ++i , i = i++, etc…)

原帖: http://stackoverflow.com/questions/949433/could-anyone-explain-these-undefined-behaviors-i-i-i-i-i-etc

===============================================================================================================================

int main(int argc, char ** argv)
{

   int i = 0;
   i = i++ + ++i;
   printf("%d\n", i); // 3

   i = 1;
   i = (i++);
   printf("%d\n", i); // 2 Should be 1, no ?

   volatile int u = 0;// 此运行结果可以参考: http://blog.csdn.net/kissmonx/article/details/8031885 
   u = u++ + ++u;
   printf("%d\n", u); // 1

   u = 1;
   u = (u++);
   printf("%d\n", u); // 2 Should also be one, no ?

   register int v = 0;
   v = v++ + ++v;
   printf("%d\n", v); // 3 (Should be the same as u ?)
}

===============================================================================================================================

下面是回答. 

===============================================================================================================================

Why are these "issues"? The language clearly says that certain things lead to undefined behavior. There is no problem, there is no "should" involved. If the undefined behavior changes when one of the involved variables is declared volatile, that doesn't prove or change anything. It is undefined; you cannot reason about the behavior.

Your most interesting-loooking example, the one with

u = (u++);

is a text-book example of undefined behavior (see Wikipedia's entry on sequence points(序列点)).

Update: As of C++11, the concept of sequence points has been replaced by sequence before and sequence after, which makes certain expressions, like u = u++, have defined behavior.

===============================================================================================================================

Read this Question from the C FAQ.

Q: How can I understand complex expressions like the ones in this section, and avoid writing undefined ones? What's a "sequence point"?

A: 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:

  1. 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);
  2. at the ||&&?:, and comma operators; and
  3. 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.

These two rather opaque sentences say several things. First, they talk about operations bounded by the "previous and next sequence points"; such operations usually correspond to full expressions. (In an expression statement, the "next sequence point" is usually at the terminating semicolon, and the "previous sequence point" is at the end of the previous statement. An expression may also contain intermediate sequence points, as listed above.)

The first sentence rules out both the examples

i++ * i++

and

i = i++

from questions 3.2 and 3.3--in both cases, i has its value modified twice within the expression, i.e. between sequence points. (If we were to write a similar expression which did have an internal sequence point, such as

i++ && i++

it would be well-defined, if questionably useful.)

The second sentence can be quite difficult to understand. It turns out that it disallows code like

a[i] = i++

from question 3.1. (Actually, the other expressions we've been discussing are in violation of the second sentence, as well.) To see why, let's first look more carefully at what the Standard is trying to allow and disallow.

Clearly, expressions like

a = b

and

c = d + e

which read some values and use them to write others, are well-defined and legal. Clearly, [footnote] expressions like

i = i++

which modify the same value twice are abominations which needn't be allowed (or in any case, needn't be well-defined, i.e. we don't have to figure out a way to say what they do, and compilers don't have to support them). Expressions like these are disallowed by the first sentence.

It's also clear [footnote] that we'd like to disallow expressions like

a[i] = i++

which modify i and use it along the way, but not disallow expressions like

i = i + 1

which use and modify i but only modify it later when it's reasonably easy to ensure that the final store of the final value (into i, in this case) doesn't interfere with the earlier accesses.

And that's what the second sentence says: if an object is written to within a full expression, any and all accesses to it within the same expression must be directly involved in the computation of the value to be written. This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification. For example, the old standby i = i + 1 is allowed, because the access of i is used to determine i's final value. The example

a[i] = i++

is disallowed because one of the accesses of i (the one in a[i]) has nothing to do with the value which ends up being stored in i (which happens over in i++), and so there's no good way to define--either for our understanding or the compiler's--whether the access should take place before or after the incremented value is stored. Since there's no good way to define it, the Standard declares that it is undefined, and that portable programs simply must not use such constructs.

===============================================================================================================================

I think the relevant parts of the C99 standard are 6.5 Expressions, §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. Furthermore, the prior value shall be read only to determine the value to be stored.

and 6.5.16 Assignment operators, §4:

The order of evaluation of the operands is unspecified. If an attempt is made to modify the result of an assignment operator or to access it after the next sequence point, the behavior is undefined.

===============================================================================================================================

This is related to something called sequence points(序列点).

You can read more about it here basically what you have written is not allowed and has undefined behavior.

===============================================================================================================================

Just compile and disassemble your line of code, if you are so inclined to know how exactly it is you get what you are getting.

This is what I get on my machine:

$ cat evil.c
void evil(){
  int i = 0;
  i+= i++ + ++i;
}
$ gcc evil.c -c -o evil.bin
$ gdb evil.bin
(gdb) disassemble evil
Dump of assembler code for function evil:
   0x00000000 <+0>:   push   %ebp
   0x00000001 <+1>:   mov    %esp,%ebp
   0x00000003 <+3>:   sub    $0x10,%esp
   0x00000006 <+6>:   movl   $0x0,-0x4(%ebp)
   0x0000000d <+13>:  addl   $0x1,-0x4(%ebp)
   0x00000011 <+17>:  mov    -0x4(%ebp),%eax
   0x00000014 <+20>:  add    %eax,%eax
   0x00000016 <+22>:  add    %eax,-0x4(%ebp)
   0x00000019 <+25>:  addl   $0x1,-0x4(%ebp)
   0x0000001d <+29>:  leave  
   0x0000001e <+30>:  ret    
End of assembler dump.

===============================================================================================================================

===============================================================================================================================


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值