我有一行代码,
int a = 10;
a = ++a * ( ++a + 5);
我的预期输出为12 * (11 + 5) = 192
,但我 187 。尽管我知道()
中的增量运算符首先要解决,然后为什么首先解决外部的增量运算符?
答案 0 :(得分:97)
表达式从左到右进行评估。括号(和优先级)只是表达分组,它们不表示评估的顺序。
所以
11 * (12 + 5)
++a ++a
等于
187
答案 1 :(得分:29)
算术表达式的评估由三组规则控制:优先规则,关联规则和顺序规则。
优先级规则描述了当表达式混合使用不同类型的运算符时,如何将表示不足的表达式括起来。
关联性规则描述了当表达式具有相同类型的运算符时,如何将表示不足的表达式括起来。
评估顺序规则描述了表达式中每个操作数的计算顺序。
更高的优先级导致操作数与操作符分组,并不意味着操作数的评估。评估的顺序决定了表达式中子表达式的评估顺序。
我可以看到很多程序员都认为这句话
a = ++a * ( ++a + 5);
将调用未定义的行为。是的,如果不保证运营商的操作数的评估顺序,它将调用UB。
但是在java编程语言的上下文中并非如此。它在java(以及C#)中具有良好定义的行为。 Java语言规范声明:
Java编程语言保证运算符的操作数似乎在特定的评估顺序中评估,即从左到右。
例15.7.1-1。左手操作数首先评估
在以下程序中,
*
运算符具有左操作数,该操作数包含对变量的赋值和包含对同一变量的引用的右操作数。引用产生的值将反映出分配首先发生的事实。class Test1 { public static void main(String[] args) { int i = 2; int j = (i=3) * i; System.out.println(j); } }
该程序产生输出:
9
不允许评估
*
运算符生成6而不是9 。
但是,java规范仍明确规定不要编写此类代码:
建议代码不要严格依赖此规范。当每个表达式最多包含一个副作用时,代码通常更清晰,作为其最外层的操作,并且当代码不依赖于由于从左到右的表达式评估而出现哪个异常时,代码就更清晰了。
答案 2 :(得分:10)
从该片段
int a = 10;
a = ++a * ( ++a + 5);
有时,最简单的解决方案是使用javap来理解评估顺序:
0: bipush 10 // push 10 onto the stack (stack={10})
2: istore_1 // store 10 into variable 1 (a = 10, stack={})
3: iinc 1, 1 // increment local variable 1 by 1 (a = 11, stack={})
6: iload_1 // push integer in local variable 1 onto the stack (a = 11, stack = {11})
7: iinc 1, 1 // increment local variable 1 by 1 (a = 12, stack={11})
10: iload_1 // push integer in local variable 1 onto the stack (a = 12, stack = {12, 11})
11: iconst_5 // load the int value 5 onto the stack (a = 12, stack = {5, 12, 11})
12: iadd // add 5 and 12 (a = 12, stack = {17, 11})
13: imul // multiply 17 and 11 (a = 12, stack = {})
a
增加1.(第3行)// a = 11 a
增加1.(第7行)// a = 12 5
添加到a
(第12行)// a = 17 11
乘以17
(第13行)// a = 187 (10 + 1 + 1 + 5)* 11 = 187
答案 3 :(得分:2)
括号的作用是控制哪些计算值用作后续操作的操作数。它们控制执行操作的顺序,只有在操作数已经过操作之前才能评估操作的程度。考虑一下表达式:
(a()+b()) * (c()+d())
a() + (b()*c()) + d()
括号不需要(并且在Java中不能)影响调用(),b(),c()和d()的顺序。它们可能影响乘法是在d()被调用之前还是之后执行,但仅在非常罕见的情况下(例如d()调用Java Native Interface,它以Java不知道的方式改变乘法中使用的数字舍入模式将代码以任何方式知道或关心是否在d()之前或之后执行乘法。
否则,重要的是在第一种情况下,一个加法运算将作用于a()和b(),另一个作用于c()和d();乘法将作用于a()+ b()和c()+ d()。在另一种情况下,第一个乘法将作用于b()和c(),第一个加法作用于()和上述乘积,第二个加法作用于第一个和d()。
答案 4 :(得分:-1)
int a = 10;
a = ++a * ( ++a + 5);
以上类型的表达式总是以从左到右的方式进行评估,无论是C还是JAVA
在这种情况下它正在这样解决 11 *(12 + 5)导致11 * 17 = 187 // w.r.t java
但如果它我们解决了相同的表达式w.r.t C语言
然后随着评估方式的改变而改变答案
在c中首先发生预增量/预减量,所以如果" N"没有时间pre inc / dec 在表达式中,然后inc / dec将首先发生" N"没有时间
然后在表达式中变量的每个发生时都会替换相同的值,并计算表达式值,并在发生后递增/递减之后
,即a增加到11然后再增加12,因为表达式中a有两个增量,然后表达式被计算为
12 *(12 + 5)= 12 * 17 = 204 //w.r.t C-language