标题：求值顺序
关于C/C++ 表达式求值顺序
表达式求值顺序不同于运算结合性和优先级。经典例子下面是一个经典例子，被 ISO C99/ C++98 /03 三大标准明确提到：他的结果是不确定(unspecified) 的。  
i = ++i + 1;   // The behavior is unspecified 
在介绍概念之前，我们先解释一下它的结果。这个表达式( expression )包含3个子表达式( subexpression ): 
e1 =  ++i
e2 = e1  + 1
i = e2
这三个子表达式都没有顺序点( sequence point )，而 ++ i  和 i = e3 都是有副作用( side effect )的表达式。由于没有顺序点，语言不保证这两个副作用的顺序。 
更加可怕的是，如果i 是一个内建类型，并在下一个顺序点之前被改写超过一次，那么结果是未定义(undefined)的！比如本例中如果有:
int i = 0x1000fffe; 
i = ++i + 1;      // The result is undefined!! 
你也许会认为他的结果是加1 或者加2，其实更糟糕 —— 结果可能是 0x1001ffff 。他的高字节接受了一个副作用的内容，而低字节则接受了另一个副作用的内容！ 如果i 是指针，那么将很容易造成程序崩溃。
为什么要这么做呢？因为对于编译器提供商来说，未确定的顺序对优化有相当重要的作用。比如，一个常见的优化策略是“减少寄存器占用和临时对象”。编译器可以重新组织表达式的求值，以便尽量不使用额外的寄存器以及临时变量。 更加严格的说，即使是编译器提供商也无法完全彻底序列化指令（比如无法严格规定读和写的顺序），因为CPU本身有权利修改指令顺序，以便达到更高的速度。
下面的术语以 ISO C99 和 C++03为准。译名为参考并附带原术语对照，如有解释不当或者错误望指正。
--------------------------------------------------------------------------------
表达式有两种功能。每个表达式都产生一个值( value )，同时可能包含副作用( side effect )，比如可能修改某些值。 
规则的核心在于 顺序点( sequence point ) [ C99 6.5 Expressions 条款2 ] [ C++03 5 Expressions 概述 条款4 ]。 这是一个结算点，语言要求这一侧的求值和副作用(除了临时对象的销毁以外)全部完成，才能进入下面的部分。 C/C++中大部分表达式都没有顺序点，只有下面五种表达式有：
1 函数。函数调用之前有一个求值顺序点。
2 &&  || 和 ?:  这三个包含逻辑的表达式。其左侧逻辑完成后有一个求值顺序点。
3 逗号表达式。逗号左侧有一个求值顺序点。 
注意，他们都只有一个求值顺序点，2和3的右侧运算结束后并没有求值顺序点。
在两个顺序点之间，子表达式求值和副作用的顺序是不确定的。假如代码的结果与求值和副作用发生顺序相关，我们称这样的代码有不确定的行为(unspecified behavior)。 而且，假如期间对一个内建类型执行一次以上的写操作，则是未定义行为(undefined behavior)——我们知道，未定义行为带来最好的后果是让你的程序立即崩掉。
n = n++;                               // 两个副作用，对于内建对象产生是未定义行为
几乎所有表达式，求值顺序都不确定。比如，下面的加法， f1 f2 f3的调用顺序是任意的:
n = f1() + f2() + f3();            // f1 f2 f3 调用顺序任意
而函数也只在实际调用前有一个求值顺序点。所以，常见于早期 C 语言教材的这类题目，是错题：
printf("%d",--a+b,--b+a);    // --a + b 和 --b + a 这两个子表达式，求值顺序不确定
天啊，甚至可能出现未定义行为？那么坚决不写与实现相关的代码是最好的对策。即使是不确定行为(比如函数调用时) 只要没有顺序点编译器怎么做方便就怎么做。 有些人认为函数调用参数求值与入栈顺序相关，这是一种误导。这个东西要解释，无异于事后诸葛亮：
void f( int i1, int i2, int i3, int i4 ){
cout<< i1 << ' ' << i2 << ' ' << i3 << ' ' << i4 << endl;
}
int main(){
int i = 0;
f( i++, i++, i++, i++ );
}
这个有四个表达式求值，同时每个表达式都有负作用。这八个操作顺序是任意的，那么结果如何？未定义。
请用 VC7.1 Debug和 Release 分别测试这同一份代码，结果是不同的: 
0 0 0 0    [release]
3 2 1 0    [debug]
事实上，鉴于前面的讨论，如果换一些其他初始值，这里甚至会出现错位而得到千奇百怪的诡异结果。其他例子再看看C/C++标准中的其他经典例子：
[C99] 6.5.2.2 Function call 
条款12 EXAMPLE 在下面的函数调用中:
(*pf[f1()])  ( f2(), f3() + f4() )
函数 f1 f2 f3 和f4 可能以任何顺序被调用。 但是，所有副作用都必须在那个 pf[ f1() ] 返回的函数指针产生的调用前完成。
[C++03] 5 Expressions 概论4
i  = v[i++];        // the behavior is unspecified
i = 7, i++, i++; // i becomes 9 ( 译注: 赋值表达式比逗号表达式优先级高 )
i = ++i + 1;      // the behavior is unspecified
i = i + 1;           // the value of i is incremented
--------------------------------------------------------------------------------
More Effective C++ 告诫我们， 千万不要重载 &&, || 和, 操作符[ MEC ，条款7 ]。为什么？
以逗号操作符为例，每个逗号左侧有一个求值顺序点。假如ar是一个普通的对象，下面的做法是无歧义的：
ar[ i ], ++i ;
但是，如果ar[ i ] 返回一个 class A 对象或引用，而它重载了 operator,  那么结果不妙了。那么，上面的语句实际上是一个函数调用：
ar[ i ].operator, ( ++i );
C/C++ 中，函数只在调用前有一个求值顺序点。所以 ar                         和 ++i 的求值、以及 ++i 副作用的顺序是任意的。这会引起混乱。
更可怕的是，重载 && 和 || 。 大家已经习惯了其速死算法： 如果左侧求值已经决定了最终结果，则右侧不会被求值。而且大家很依赖这个行为，比如是C风格字符串拷贝常常这样写：
while( p && *p )
*pd++ = *p++;
假如p 为 0， 那么 *p 的行为是未定义的，可能令程序崩溃。 而 && 的求值顺序避免了这一点。 但是，如果我们重载 && 就等于下面的做法：
exp1 .operator && ( exp2 ) 
现在不仅仅是求值混乱了。无论exp1是什么结果，exp2 必然会被求值。
--------------------------------------------------------------------------------
模糊语义
最后这里有篇更深入的帖子：C++中的求值|副作用|序列点所导致的模糊语义，推荐看看。
作者在google讨论组上发了篇帖子，看了简直会让你对求值顺序和副作用发疯。比如，下面这个表达式结果居然是不确定的？
cout << "x = " << itoa(42,buf,10) << ", y = " << itoa(43,buf,10); 
大家要小心别中招啊...  另外有一个更加难以察觉的例子：
void f    ( auto_ptr<int> );
void f_2( auto_ptr<int>,  int );
f( auto_ptr<int>( new int ) );    // exception save
f( auto_ptr<int>( new int ), g() );  // !! not exception save
第一个例子是异常安全的。new int 返回一个指针 —— 如果他失败那么内存根本不会分配。 auto_ptr<> 如果失败（如果的话），那么它有能力删除这个指针。
第二个例子则不然，如果g()抛出异常，那么他有可能导致 memleak —— 你看，若求值碰巧以这个顺序进行：
1  int* temp  = new int
2  g()
3  auto_ptr( temp )
那么当 g() 抛出异常的时候，temp 是一个裸指针，不会有任何机制帮助它释放其内存！