------------------------------------------------
道歉:
写这个东西,本来是一时兴起,闹着玩的.不想批评居多.
本来是不准备再理会这个贴了,但忍不住又看了下.
coputer00提示<3><4>没法编译,我找了KEIL ARM RV 编译了下,确实如此.
不过如果是C++文件,是可以编译通过的.
再尝试了VC6.0, VC2003, C++ builder, 这2句都不能在C下编译,只能在C++下编译通过.
我写这个东西的时候,参考的是ISO/IEC C++ 14882 标准,都是在C++下编译的.
所以没发现C下不能编译.
我重新查阅了ISO/IEC C 9899 标准,象++i这样的前缀式,并没有明确说明它的返回值可以作为左值使用.
对此我表示歉意.
我更正我的说法如下:
以下的述说都基于ISO/IEC C++, 于C下,下面的说法有可能是错误的.
再次表示歉意.
附: C标准
Constraints
The operand of the prefix increment or decrement operator shall have qualified or
unqualified real or pointer type and shall be a modifiable lvalue.
Semantics
The value of the operand of the prefix ++ operator is incremented. The result is the new
value of the operand after incrementation. The expression ++E is equivalent to (E+=1).
See the discussions of additive operators and compound assignment for information on
constraints, types, side effects, and conversions and the effects of operations on pointers.
C++标准:
The operand of prefix ++ is modified by adding 1, or set to true if it is bool (this use is deprecated).
The operand shall be a modifiable lvalue. The type of the operand shall be an arithmetic type or a pointer
to a completely-defined object type. The value is the new value of the operand; it is an lvalue. If x is not
of type bool, the expression ++x is equivalent to x+=1. [Note: see the discussions of addition (5.7) and
assignment operators (5.17) for information on conversions. ]
------------------------------------------------
如果要知道以上语句的答案,需要先说明几个概念.
1) 左值和右值
左值是可以写在=号左边的表达式,必须是一个非const的变量.
右值是可以写在=号右边的表达式,可以是一个常量,一个字面数字,或者说一个任意的可以求值的表达式.
一个可以作为左值的表达式,同时也是可以作为右值使用的.
不是有人建议(或者说提倡)你这么做嘛
if(exp == 5) ....
写成
if(5 == exp) ....
这就是利用了象5这么样的字面数字,只能作为右值使用, 如果你不小心写成了 if(5 = exp) 那么编译器认为这是一个错误.
虽然我不喜欢这么做,但我不否认这么做有它的好处.
2) 临时变量
这里所说的临时变量,不是那种类似 tmp = i; i = j; j=tmp; 的临时变量,
是指那种由编译器暗中产生的"无名字"的临时变量.
比方: int func(...) {...};
int x = func();
这里实际上有一个临时变量的产生, 就是func()的返回值,它之后作为一个右值,赋给x;
象这种临时变量, 它是不能作为左值使用的,你不能写成这样:
func() = 5; //错误, func()的返回值是一个临时变量,不能作为左值使用.
3) 逗号(,)运算符
还记得','运算符吧, 它是一系列的表达式, 之间用','隔开.
运算规则是,从左到右依次计算每个表达式,之后返回最右边的那个表达式.
比如:
int i, j, k, l;
i = j=2, k=3, l=4;
最后i的结果等于4.
4) 运算符优先级的问题
*(不是乘,是取值的那个符号), ++, --, 都是同一个优先级,结合规律是 右->左
言规正转,开始说递增/递减运算符.
前置的++, 先递增,后取值. 后置的++, 先取值,后递增.
当你写下 ++x , 实际上它等同于表达式:
x=x+1, x <a>
x++, 等同于表达式:
tmp=x, x=x+1, tmp <b>
!! 注意<b>中那个tmp, 是一个由编译器产生的临时变量.
<a>和<b>中有个重要的区别, <a>返回的是一个可以作为左值使用的变量, <b>返回的是一个只能作为右值使用的临时变量.
!!而且,因为有x=x+1的动作,所以x本身必定是一个可以作为左值的表达式.
现在分析下 x++++ 和 ++++x
x++++ => (x)(++)(++)
右边的那个(++), 因为它右边没有任何表达式了,所以它只能是一个后置的++, 它作用于 (x)(++) 身上,
而(x)(++)本身也是一个后置式的++, 它的返回值是一个临时的变量,不能作为左值使用,所以这个语句不能通过编译.
++++x => (++)(++)(x)
很显然, 右边的那个(++)是一个前置的++, 作用于(x), 返回是一个可以作为左值的值,
其返回结果作为左边那个(++)的操作数.
所以这个语句是合法的, 执行完后x加了2次.
最后,对前面的题进行解答:
1> i++++; // 不能编译
2> ++i++; // 不能编译, 因为++运算符是右结合的, 所以先计算i++, 结果作为左边++的操作数.
// 但i++的返回结果是不能作为左值使用的.
3> (++i)++; // OK. (++i)的结果作为 后置++的操作数
// 结果 i 加了2次
4> ++i = 5; // OK; ++i的返回结果是可以作为左值的
// 最后i的结果等于5
5> i++ = 5; // 不能编译. i++ 不能作为左值使用
6> i = *p++; // => i= *((p)(++)); (因为++, *是右结合的)
// 先计算p++, 返回一个p累加前的指针, 作为 * 的操作数
// 结果就是累加前的那个p指针指向的对象的内容
7> i = *(p++); // 因右结合的关系,同6>的表示是一样的,结果也是6>的结果
8> i = (*p)++; // 先计算*p, 得到p所指向的对象, 之后p指向的对象+1, p本身保持不变
9> i = ++f(); // 不能编译. f()的返回是一个临时变量,不能作为左值使用.
====================================== <剧终>