我正在研究Java前缀运算符并遇到了这种行为
i = +--j //does not give an error
i = -++j //does not give an error
i = ---j //gives an error
i = +++j //gives an error
为什么会这样?
答案 0 :(得分:6)
由于int *new_array(void) {
static const int arr[] = { 1, 2, 3, 4, 5 };
int *result = arr; /* or &arr[0] */
return result; /* or "return arr;" */
}
和+
(或+++
和-
)都是左关联的,--
被评估为+++j
。由于++(+j)
只能应用于l值(即变量)而++
不是l值(变量),因此会出现编译错误。
您可以使用括号来解决此问题:+j
。
答案 1 :(得分:2)
编译器使用贪婪的从左到右的标记选择。因此,当它看到+--j
时,作为有效令牌的最长序列是+
,因为+-
不是有效令牌,所以它需要+
作为第一个令牌。然后它查看可以识别为令牌的下一个最大的事物,即--j
。
结果是+ --j
对于---j
,它会将--
视为最长的有效令牌,然后-j
作为下一个有效令牌,并尝试将它们组合为-- -j
,作为@ Mureinik指出,无效。
答案 2 :(得分:1)
在编译器甚至到达知道哪些运算符存在的点之前,它必须解析它们。我可以看到---j
的3种可能的解析:
- - -j // 3 unary - operators
-- -j // predecrement -- followed by unary -
- --j // unary - followed by predecrement --
+++j
的案例是等效的,其中包含前递增++
和一元+
。
为什么Java将其解释为--
后跟-
,第二种情况,这是唯一一个在语法上无效的情况?编译器通常是 greedy 。 Section 3.2 of the JLS州:
使用以下三个词汇翻译步骤将原始Unicode字符流转换为一系列标记,这些步骤依次应用:
...
每个步骤使用最长的翻译,即使结果最终没有制作正确的节目,而另一个词汇翻译。有一个例外:如果词汇翻译发生在类型上下文(§4.11)中,并且输入流有两个或更多连续的>非>后面跟着的字符字符,然后每个>必须将字符转换为数字比较运算符的标记>。
(大胆强调我的)
编译器贪婪地看到两个-
个字符并立即将其声明为--
令牌,而不考虑接下来的第三个-
。
这与运算符关联性甚至运算符优先级无关,也与语法分析有关。
正如@Mureinik已经提到的那样,正确放置澄清括号将迫使编译器正确解析它。但它通过将字符分解为不同的标记来实现这一点,而不是通过改变操作的优先级来实现。
表达式-++j
不受编译器贪婪的影响; -+
不是有效令牌,因此会将其正确解析为令牌-
,后跟令牌++
,对于表达式+--j
也是如此。