|
5#
楼主 |
发表于 19.3.2003 11:39:07
|
只看该作者
总结: <span style='font-size:30pt;line-height:100%'>转</span><br><br>发信人: thrust (小猪哼哼*减肥中), 信区: Programming <br>标 题: 总结修订版 <br>发信站: The unknown SPACE (Fri Feb 14 15:13:58 2003), 转信 <br> <br>看看也差不多了, 牛鬼蛇神们也都出来了. 俺总结两句, 大家觉得不对可以指出来.<br>我手头没有C的标准, 是按照C++的来写的, 我想大概对C也好用. 如果不是, 请告<br>诉我.<br> <br>C/C++语言的表达式计算顺序由好几个因素决定. 第一个, 最明显的, 如果A运算<br>的操作数是B运算的结果, 那么肯定B要比A先算. 比如a+b*c, 那肯定先做乘法.<br>第二个, 一个表达式中可能有一个到多个sequence point. 翻译一下, 就是说,<br>规定所有应该在point之前的运算,必须在所有point之后的运算之前完成. 至于<br>point之前的这些运算中哪个先做, 哪个后做, 是没有规定的. 其余的项目未作<br>规定, 如果产生二义性, 那说明你的表达式不合理. 这里我说的不合理, 意思是<br>标准未经定义的行为, 编译器可以根据自己的需要进行解释, 倒不一定会产生编<br>译错误.<br>比如说, 下面的表达式是不合理的:<br>a=(++i)*(i+1);<br>// 中间的乘法肯定要最后做. 但是左右两边先算哪个? 先算++i, 那么式子<br>// 变成(i+1)*(i+2), i++, 先算i+1, 那么式子变成(i+1)*(i+1), i++,<br>// 有二义性.<br>*++b=*b*2;<br><br><br>// 中间的赋值是最后做, 但是左右先算哪个? 不知道.<br> <br>有一点要注意: 括号并不代表先被计算. 比如,<br>a=(++i)+i;<br>// 是一个不合理的式子. 并不是说, ++i外面有括号, 就会被先计算了;<br>// 事实上, C++标准根本就没有规定precedence, 完全是用语法来规定的.<br>// 而(++i)和i同为primary expression, 它们两个哪个先算是没规定的.<br> <br>按照规定, sequence point 有这么几类:<br>第一, 表达式运算完成是一个sequence point.<br>第二, 函数调用之前, 所有的参数计算完毕, 是一个sequence point.<br>第三, 函数返回值被copy之后, 是一个sequence point.<br>第四, a&&b, a||b, a?b:c, a,b四种运算符: 在a算完之后是一个sequence point.<br> <br>解释一下. 第一条没什么好说的. 第二条, 函数的参数计算完后才开始执行函数.<br>第三条, 函数的返回值copy完以后, 才开始算其它的部分. 把二和三合起来看,<br>一个函数的执行和其返回值的copy是一个整体, 中间不插入对表达式其它部分的<br>计算. 这里的函数是广义的, 包括操作符重载, inline function, 隐式的类型转<br>换函数等等. 第四条, 说明第一个操作数总是比其它的要先计算.<br> <br>另一条很重要的规定: 在两个sequence point之间, 一个标量最多只能被修改一次.<br>如果修改一次以上, 则其结果未定义. 因此如下的表达式都是不合理的:<br><br><br>a=(++i)+(++i);<br>// 这个表达式只有最后一个sequence point, 因此修改两次i是不合理的.<br>i+=++i;<br>// 同上面一样. ++i和i+=...修改了两次i.<br>而以下的表达式是合理的:<br>a=(++i)==(++j);<br>// i和j各被修改了一次.<br>a=(++i) && (++i);<br>// 这个式子中间有一个sequence point: 在算完第一个++i之后. 因此, 这个<br>// 表达式被分成了两段, 每段修改了一次i, 是允许的.<br>a=(++i==++j) && (++i==++j);<br>// 同上.<br> <br>牵涉到函数调用是一样的道理. 比如说:<br>cout<<++a<<++a;<br>// 其实是翻译成:<br>operator<< (operator<< (cout,++a),++a);<br>// 这是个不合理的式子. 函数调用的参数计算顺序是未作规定的; 外层函数调用,<br>// 可以先计算第一个参数, 又是一个函数调用, 使a+1并遇上两个sequence point;<br>// 再算第二个参数, a++并再遇上两个sequence point, 这里没有问题.<br>// 但是如果先计算第二个参数, 那么a++, 然后计算第一个参数, 发现函数调用,<br>// 分别计算里层的参数, a又加一, 其值改变了两次, 这结果就是未定义的.<br><br><br>修订:<br>我见过很多教科书上说前缀++和后缀++的区别: 前缀++在整个表达式计算前<br>先加, 后缀++在整个表达式计算后再加. 其实这是片面的. 前后缀的区别仅仅在于,<br>(++i)的结果是i+1,而(i++)的结果是i. (当然最后i都被加了1, 但那只是side effect.)<br>并不存在前缀是在整个表达式之前计算, 后缀是在表达式之后计算的说法, 编<br>译器可以在满足sequence point 限制的条件下, 自由地选择+1的时间.<br>自减运算--也是类似的.<br>另一个区别是, 前缀++/--的结果是个lvalue, 而后缀的结果是rvalue.<br>事实上, ++i和i+=1是等价的. |
|