如果括号具有更高的优先级,那么为什么增加算子先解决?
我有一个单行代码,
int a = 10; a = ++a * ( ++a + 5);
我的预期产出是12 * (11 + 5) = 192
,但我有187 。 就我所知,内部()
内的增量操作符首先要解决,那么为什么外面的一个先解决?
expression式从左到右进行评估。 括号(和优先级)只是表示分组,并不表示评估的顺序。
所以
11 * (12 + 5) ++a ++a
等于
187
引用Eric Lippert的博客 :
算术expression式的评估由三组规则控制:优先规则,关联规则和顺序规则。
优先规则描述了当expression式混合不同types的运算符时,如何将不完整的expression式加括号。
关联性规则描述了当expression式包含一堆相同types的操作符时,如何将不恰当的expression式加括号。
评估规则的顺序描述了评估expression式中每个操作数的顺序。
更高的优先级导致操作数与运算符分组,并不意味着对操作数的评估。 决定expression式中子expression式评估顺序的评估顺序。
更新:
正如我所看到的许多程序员认为这种说法
a = ++a * ( ++a + 5);
会调用未定义的行为。 是的,如果没有运营商操作数评估顺序的保证,它会调用UB。
但是在java编程语言的情况下这是不正确的。 它在java(以及C#)中有良好的行为。 Java语言规范指出:
15.7。 评估顺序
Java编程语言保证操作符的操作数看起来是按照特定的评估顺序,即从左到右进行评估的 。
例15.7.1-1。 左手操作数首先被评估
在下面的程序中,
*
运算符的左侧操作数包含对variables的赋值,右侧的操作数包含对同一variables的引用。 参考文献所产生的价值将反映出这项工作首先发生的事实。class Test1 { public static void main(String[] args) { int i = 2; int j = (i=3) * i; System.out.println(j); } }
这个程序产生输出:
9
不允许评估
*
运算符产生6而不是9 。
但是,java规范还是明确指出不写这样的代码:
build议代码不要严格依赖于这个规范 。 当每个expression式至多包含一个副作用时,代码通常会更清晰,作为其最外层的操作,并且代码并不完全依赖于由expression式的左到右评估而产生的exception。
从这个片段
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
(第12行)// a = 17 - 乘
11
到17
(13行)// a = 187
(10 + 1 + 1 + 5)* 11 = 187
括号的作用是控制哪些计算值用作后续操作的操作数。 它们控制操作的执行顺序,只有在操作数达到操作数后才能对操作进行评估。 考虑expression式:
(a()+b()) * (c()+d()) a() + (b()*c()) + d()
括号不需要(在Java中不能)影响a(),b(),c()和d()被调用的顺序。 它们可能会影响乘法是在d()被调用之前还是之后执行的,但是仅在极less数情况下(例如d()调用Java本地接口,以Java方式不知道的方式改变乘法中使用的数字舍入模式约)将代码有任何方式知道或关心乘法是否在d()之前或之后执行。
否则,重要的是,在第一种情况下,一个加法操作将作用于()和b(),另一个作用于c()和d()。 乘法将作用于()+ b()和c()+ d()。 在另一种情况下,第一次乘法将作用在b()和c()上,第一次加上()和上述乘积,第二次加上第一次和和d()。
int a = 10; a = ++a * ( ++a + 5);
以上types的expression式总是以左至右的方式来评估它是C还是JAVA
在这种情况下,它正在解决像这样的11 *(12 + 5),这导致11 * 17 = 187 // wrt java
但如果我们解决与C语言相同的expression式
那么答案会随着评估方式的变化而变化
在第一个预先增加/预先递减的情况下发生,所以如果在expression式中没有preinc / dec之前的“N”,那么inc / dec将首先发生“N”次
那么在expression式中的每个variables的出现中将代入相同的值并且计算expression式值,并且之后发生增量/减量
即a递增到11,然后再递增12,因为在expression式中有两个递增,然后expression式被评估为
12 *(12 + 5)= 12 * 17 = 204 //关于C语言