噜噜哒
让我说得很清楚,因为人们一直误解这一点:子表达式的求值顺序是独立结合性和优先级..关联性和优先级确定操作者被执行但是不要确定子表达式都是经过评估的。你的问题是关于子表达式都是经过评估的。考虑A() + B() + C() * D()..乘法比加法有更高的优先级,加法是左联想的,所以这相当于(A() + B()) + (C() * D())但知道这只是告诉你,第一个加法将发生在第二个加法之前,乘法将发生在第二个加法之前。它没有告诉您将按什么顺序调用A()、B()、C()和D()!(它也不告诉您乘法是在第一个加法之前还是之后进行。)完全有可能遵守优先性和结合性将其汇编为:
d = D() // these four computations can happen in any orderb = B()c = C()a = A()sum = a + b
// these two computations can happen in any orderproduct = c * d
result = sum + product // this has to happen las
t所有的优先和结合的规则都在那里-第一个加法发生在第二个加法之前,乘法发生在第二个加法之前。显然,我们可以调用A()、B()、C()和D()在任何秩序和仍然遵守优先和联想的规则!我们需要一个规则不相干来解释计算子表达式的顺序。Java(和C#)中的相关规则是“从左到右计算子表达式”。因为A()出现在C()的左边,所以首先计算A(),不管C()涉及乘法,而A()只涉及加法。所以现在你有足够的信息来回答你的问题了。在……里面a[b] = b = 0结合性的规则说这是a[b] = (b = 0);但这并不意味着b=0先跑!优先级规则说索引比赋值优先级更高,但是这并不意味着索引器在最右边赋值之前运行。.(更新:本答复的较早版本在下面这一节中有一些小的、实际上并不重要的遗漏,我对此作了更正。我还写了一篇博客文章,描述为什么在Java和C#中这些规则是合理的:https:/ericlippert.com/2019/01/18/indexer-Error-case/)优先性和结合性只告诉我们零赋值到b一定要发生以前分配给a[b],因为零的赋值计算在索引操作中分配的值。优先级和联想性本身并不说明a[b]被评估以前或后这个b=0.同样,这与:A()[B()] = C()-我们所知道的是,索引必须发生在分配之前.我们不知道A()、B()还是C()是先运行的基于优先级和结合性..我们需要另一条规则来告诉我们。规则是,“当你对首先要做什么有选择时,总是从左到右”。然而,在这个特定的场景中有一个有趣的问题。由空集合或超出范围的索引引起的抛出异常的副作用是赋值左侧计算的一部分,还是赋值本身计算的一部分?Java选择后者。(当然,这是唯一重要的区别如果代码已经错误,因为正确的代码不会取消引用NULL,或者首先传递一个糟糕的索引。)那会发生什么呢?这个a[b]的左边b=0,所以a[b]跑第一,导致a[1]..但是,检查效度该索引操作延迟。然后b=0会发生的。那么验证a是有效的而且a[1]在范围内将值赋值给a[1]最后发生。所以,虽然在这个专一对于那些不应该出现在正确代码中的罕见错误情况,首先要考虑一些微妙之处,通常您可以这样做:事情发生在左边,而不是右边。..这就是你要找的规矩。关于优先权和联想性的讨论既令人困惑,也不相关。人们搞错了一直即使是那些应该更了解的人。我编辑过太多了编程书错误地陈述了规则,所以很多人对优先级/结合性和评估顺序之间的关系有着完全不正确的信念-也就是说,在现实中没有这样的关系,它们是独立的。