再也不要讨论类似(i++)+(i++)+(i++)表达式的值了!
2012-11-11 16:33
239 查看
研究
自增操作符的误用
C语言标准并没有详细地规定一个表达式该如何求值,如3 * a + 5 * b中先算哪个乘法;表达式a = f1( ) + f2( )中先调用函数f1还是先调用函数f2;表达式( ) * ( )中,先对哪个括号里的子表达式求值。虽然不同的编译系统对子表达式的求值顺序有着不同的优化原则,但是,通常情况下一个表达式在不同的编译系统中会表现为相同的值。自增操作符的作用是让变量自增1,但是,误用自增操作符会引起一些麻烦,具体地说,就是会出现一些在不同的编译系统中可能有不同值的表达式。
如有int i = 2时,表达式(i++) + (i++) + (i++)的值是多少呢?
子表达式i++求值时一方面表达式的值是2,另一方面求值操作将使变量i的值自增1,当变量i还是其它子表达式的操作数时,问题就来了:变量i的值是原值还是加1后的新值呢?
在VC6.0中,第一个子表达式i++求完值后,其它子表达式中出现的变量i的值还没有改变,依然是2。表达式(i++) + (i++) + (i++)的值为6(2+2+2),求完值后,变量i会执行自增操作3次,其值会变成5。
在TC中,第一个子表达式i++求完值后,变量i会立即执行自增操作,因此,第二个子表达式中变量i的值已经是3了。表达式(i++) + (i++) + (i++)的值为9(2+3+4)。
如何评价这个“值不确定”的表达式呢?
首先,这个表达式不“合法”。严格地说C语言程序中不能出现类似的表达式,它是非法的,虽然它能通过编译系统的检查并也能输出一个结果。
其次,程序没有必要出现这样的表达式。可读性是程序最重要的属性,如果需要值为9的表达式,则原表达式可改写为i+(i+1)+(i+2)和i+=3两个表达式;如果需要值为6的表达式,则原表达式可改写为i+i+i和i+=3两个表达式。改写后的表达式既简单又具有确定的值。
误用自增操作符也会影响函数的输出。如有int j = 3,函数调用语句printf("%d,%d\n", j, j++);的输出值也不确定。调用函数时,C语言标准没有明确规定参数的求值顺序,但TC和VC都按自右向左的顺序对实参求值。在VC中,先计算表达式j++的值为3,但它不马上执行自增操作,因此,对实参j求值的结果还是3,输出结果为3,3。在TC中,先计算表达式j++的值为3,然后马上执行自增操作,因此,对实参j求值的结果是4,输出结果为4,3。
编程时不要以这种方式使用自增操作符。当希望输出3,3时,可以将上面的语句改写为printf ("%d,%d\n", j, j);和++j;两条语句。当希望输出4,3时,可以将上面的语句改写为i = j++;和printf("%d,%d\n", j, i);两条语句。
强调:
表达式中出现了子表达式i++(或++i或--i或i--)后,变量i就不要再出现在其它子表达式中了。
注意:
VC6.0采用了不同的原则处理前置和后置自增(自减)操作符。前置自增操作符会先执行自增操作,如有int i = 2时,表达式++i + i的值为6(3+3),但是表达式(++i) + (++i)的值却是8(4+4即先执行两次自增操作,第一次使变量i的值变为3,第二次使变量i的值变为4,然后再求值)。表达式(++i) + (++i) + (++i)的值是多少呢?(4+4+5=13,子表达式(++i) +求完值后,原表达式变成了8+(++i)。)
讨论:
(1) 如何看待C语言中值与编译系统相关的表达式?
(2) 使用自增操作符时应避免出现什么情况?
(3) 在VC6.0中表达式++i和表达式i++都可以使变量i自增1,但它们的执行过程相同吗?
提示:
(1) 此类表达式的共同特点是可读性和实用性都非常差。
自增操作符的误用
C语言标准并没有详细地规定一个表达式该如何求值,如3 * a + 5 * b中先算哪个乘法;表达式a = f1( ) + f2( )中先调用函数f1还是先调用函数f2;表达式( ) * ( )中,先对哪个括号里的子表达式求值。虽然不同的编译系统对子表达式的求值顺序有着不同的优化原则,但是,通常情况下一个表达式在不同的编译系统中会表现为相同的值。自增操作符的作用是让变量自增1,但是,误用自增操作符会引起一些麻烦,具体地说,就是会出现一些在不同的编译系统中可能有不同值的表达式。
如有int i = 2时,表达式(i++) + (i++) + (i++)的值是多少呢?
子表达式i++求值时一方面表达式的值是2,另一方面求值操作将使变量i的值自增1,当变量i还是其它子表达式的操作数时,问题就来了:变量i的值是原值还是加1后的新值呢?
在VC6.0中,第一个子表达式i++求完值后,其它子表达式中出现的变量i的值还没有改变,依然是2。表达式(i++) + (i++) + (i++)的值为6(2+2+2),求完值后,变量i会执行自增操作3次,其值会变成5。
在TC中,第一个子表达式i++求完值后,变量i会立即执行自增操作,因此,第二个子表达式中变量i的值已经是3了。表达式(i++) + (i++) + (i++)的值为9(2+3+4)。
如何评价这个“值不确定”的表达式呢?
首先,这个表达式不“合法”。严格地说C语言程序中不能出现类似的表达式,它是非法的,虽然它能通过编译系统的检查并也能输出一个结果。
其次,程序没有必要出现这样的表达式。可读性是程序最重要的属性,如果需要值为9的表达式,则原表达式可改写为i+(i+1)+(i+2)和i+=3两个表达式;如果需要值为6的表达式,则原表达式可改写为i+i+i和i+=3两个表达式。改写后的表达式既简单又具有确定的值。
误用自增操作符也会影响函数的输出。如有int j = 3,函数调用语句printf("%d,%d\n", j, j++);的输出值也不确定。调用函数时,C语言标准没有明确规定参数的求值顺序,但TC和VC都按自右向左的顺序对实参求值。在VC中,先计算表达式j++的值为3,但它不马上执行自增操作,因此,对实参j求值的结果还是3,输出结果为3,3。在TC中,先计算表达式j++的值为3,然后马上执行自增操作,因此,对实参j求值的结果是4,输出结果为4,3。
编程时不要以这种方式使用自增操作符。当希望输出3,3时,可以将上面的语句改写为printf ("%d,%d\n", j, j);和++j;两条语句。当希望输出4,3时,可以将上面的语句改写为i = j++;和printf("%d,%d\n", j, i);两条语句。
强调:
表达式中出现了子表达式i++(或++i或--i或i--)后,变量i就不要再出现在其它子表达式中了。
注意:
VC6.0采用了不同的原则处理前置和后置自增(自减)操作符。前置自增操作符会先执行自增操作,如有int i = 2时,表达式++i + i的值为6(3+3),但是表达式(++i) + (++i)的值却是8(4+4即先执行两次自增操作,第一次使变量i的值变为3,第二次使变量i的值变为4,然后再求值)。表达式(++i) + (++i) + (++i)的值是多少呢?(4+4+5=13,子表达式(++i) +求完值后,原表达式变成了8+(++i)。)
讨论:
(1) 如何看待C语言中值与编译系统相关的表达式?
(2) 使用自增操作符时应避免出现什么情况?
(3) 在VC6.0中表达式++i和表达式i++都可以使变量i自增1,但它们的执行过程相同吗?
提示:
(1) 此类表达式的共同特点是可读性和实用性都非常差。
相关文章推荐
- 朗姆达表达式类似IN查询条件
- Java regex正则表达式类似死循环问题
- 关于php没有类似java标签的讨论
- c#中实现类似js的Eval|.NET中执行Javascript(表达式是字符串的计算)
- 正则表达式中量词贪婪型和勉强型的讨论(Java语言描述)
- 不要滥造正则表达式
- 给出一个表达式以及表达式里面所有变量的值。求出这个表达式的值。类似javascript中的Eval().
- 【ZZ】c/c++程序一个语句中不要有两个表达式有副作用
- mysql 值/表达式判断函数ifnull()与if() [类似ms sql server的isnull()]
- 四种Eval测试结果:不要用CodeDom做大批量的表达式四则运算
- jQuery扩展半Lambda表达式 类似Linq的Where
- 在(ASP+MSSQL)全文本搜索中如何用正则表达式实现类似百度的显示效果?
- Android实现通用的ActivityGroup(效果类似Android微博客户端主界面),强烈建议不要再使用TabActivity
- 关于表格的正则表达式讨论(表格)
- Python语法 - yield表达式(类似 m = yield i )
- Android实现通用的ActivityGroup(效果类似Android微博客户端主界面),强烈建议不要再使用TabActivity
- 不要在eclipse的debug模式下watch某些方法表达式
- 让JavaScript拥有类似Lambda表达式编程能力的方法
- 正规表达式 在查找替换中的使用 一个看类似变态问题的解决
- 改善C++ 程序的150个建议学习之建议3:对表达式计算顺序不要想当然