为什么i | = j | = k | =(j + = i) - - (k +++ k) - - (i = + j)== 11?

-80 java operators operator-precedence

我在一个我开始研究的项目中遇到了这段代码.最初的开发人员已经不再可用,我无法理解它.

k = (j = (i = 0) + 2) + 1;
return i|= j|= k|= (j+= i) - - (k+++k) - - (i =+j);
Run Code Online (Sandbox Code Playgroud)

它产生的价值11.这是如何运作的?

什么是=+运营商?

什么是+++运营商?

什么是- -运营商?

什么是|=运营商?

Dan*_*her 202

什么是=+运营商?

这是两个运算符,一个赋值运算符,=一个一元加号+,它什么都不做.

你错了并且意味着compund赋值运算符+=

什么是+++运营商?

还有两个运算符,一个后增量,++一个加法,+(根据最大蒙克规则,选择最长的有效令牌,如果选择了最短的有效令牌,它将成为一个加法和两个一元加号).

什么是- -运营商?

再两个运算符,一个减法和一个一元减(否定).

什么是|=运营商?

复合赋值,按位[或者,在boolean值的情况下,逻辑运算]左侧值与右侧值并将其存储在左侧变量中.

a |= b;
Run Code Online (Sandbox Code Playgroud)

几乎相当于

a = a | b;
Run Code Online (Sandbox Code Playgroud)

但是左侧操作数只被评估一次,后者可能需要一个显式的强制转换,而前者则不需要.

k = (j = (i = 0) + 2) + 1;
return i|= j|= k|= (j+= i) - - (k+++k) - - (i =+j);
Run Code Online (Sandbox Code Playgroud)

它产生的值为11.这是如何工作的?

第一行相当于

i = 0;
j = i+2;
k = j+1;
Run Code Online (Sandbox Code Playgroud)

赋值(i = 0例如)计算存储的值(在i此处).

下一行是适当的间距,并添加了隐式括号

return i |= (j |= (k |= (((j += i) - (-(k++ + k))) - (-(i = +j)))));
Run Code Online (Sandbox Code Playgroud)
  • i |= stuff_1:i被评估(0),stuff_1被评估,按位或被采用,结果存储在i.因为i原来是0,相当于i = stuff_1.

  • j |= stuff_2:j被评估(2),stuff_2被评估,按位或被采用,结果被存储j.

  • k |= stuff_3:k评估(3),然后stuff_3,从左到右.

    • (j += i)添加ij,存储总和j与回报j的新的价值.由于i为0,因此j不会更改,值为2.
    • (k++ + k)取旧值k(3),递增k并加上k新值(4),得到7.该值被否定,否定值(-7)从2中减去,结果为2 - (-7) = 9.
    • (i = +j)存储j(2)i的值,表达式的值也是2.该值被否定(-2)并从我们从前面的操作得到的9减去,因此stuff_3计算结果为11,副作用是

      • i现在存储的值为2
      • j现在存储的值是2(实际上没有变化,因为i最初为0)
      • k现在存储的值是4
    • k(3)的旧值按位或按11,得到11,存储在k11 中,11是值stuff_2,即k |= stuff_3.
  • j(2)的旧值与stuff_2(11)的值按位或按下,得到11.值存储在j,并且stuff_1(j |= stuff_2)的值为11.

  • i(0)的旧值按位(或)与stuff_1(11)的值按位,结果输入i,值为i |= stuff_111.然后返回该值.

  • 即将面临您的采访......"所以您是Java开发人员?请评估这个基本的Java表达式" (12认同)
  • 括号只是确定表达式树的结构.然后你有数据依赖,评估`left + right`,首先`left`和`right`必须被评估(对于`||`和`&&`,可能只有`left`).在C中,除少数例外情况外,评估顺序未指定.在Java中,它被指定为从左到右. (3认同)
  • 不.试试`int i = 2,j = 3; i | =(i + = j);`.结果是'2 | (2 + 3)= 7`.如果首先评估`i + = j`,结果将是'5 | 5 = 5` - 唉,`= +`,不是`+ =`.所以取`i = 2,j = 5; i | =(i = + j);`. (2认同)

Gle*_*est 45

最初的开发人员已经不再可用,我无法理解它.

最初的开发人员故意在代码中留下了一个曲折的任务问题.这与Daniel Fischer给出的答案完全相同,但为了清楚地解释,我将按照与评估顺序相对应的步骤进行评估.

k =(j =(i = 0)+ 2)+ 1;

括号和运算符优先级,评估为:

i = 0;    
j = i + 2;  // j = 2
k = j + 1;  // k = 3
Run Code Online (Sandbox Code Playgroud)

return i | = j | = k | =(j + = i) - - (k +++ k) - - (i = + j);

扩展"| ="运算符,这相当于:

return i = i | ( j = j | ( k = k | ( (j+= i) - - (k+++k) - - (i =+j) ) ) );
Run Code Online (Sandbox Code Playgroud)

左边的"|" 始终首先评估运算符并记住,因此将变量值替换为左侧:

return i = 0 | ( j = 2 | ( k = 3 | ( (j+= i) - - (k+++k) - - (i =+j) ) ) );
Run Code Online (Sandbox Code Playgroud)

然后评估每个"|"的右侧 运算符(带括号,运算符和从左到右的优先级):

(j+= i):   pre-increments j = j + i;            //  j = 2 + 0 = 2
           then expression evaluates to j       //  evaluates to 2                   

(k+++k):   first evaluates k++                  //  sub-expression evaluates to 3, 
                                                //  then post-increments k: k = 4 
           then evaluates (k++)+k               //  evaluates to 3+4 = 7

(i =+ j):  "=+" is not an java operator!!
           it is evaluated as "=" operator followed by unary "+" operator
           expression evaluates to +j           // evaluates to 2

k = 3 | ( (j+= i) - - (k+++k) - - (i =+j) )
  = 3 | ( 2 - -7 - -2) = 3 | 11 = (2+1) | (8+2+1) = (8+2+1) = 11


j = 2 | k
  = 2 | 11 = (2) | (8+2+1) = 8+2+1 = 11


i = 1 | j;                    
  = 1 | 11 = (1) | (8+2+1) = 8+2+1 = 11

return i;  // return 11
Run Code Online (Sandbox Code Playgroud)


Ste*_*eph 18

对我来说,最好的答案是Mike Rylander(评论中).

用它替换return 11;并提交.

我的意思是,代码不依赖于之前编写的任何内容,因此每次生成11.这是一个复杂的计算,需要时间,什么都不会产生11.所以你只需要返回11.不要保持开发者的无用代码显然对你很开心.它让我想起了一位前同事,他在代码中设置了炸弹(很少崩溃,但有时甚至崩溃),就在辞职之前......

注意:可能存在不等效的情况:如果i,j和k在方法之外可见并在其他地方重复使用.但这不太可能.

  • 在这种情况下,我完全同意.但是,"自包含"计算不应总是由单个常量替换,因为计算可以记录该值是如何从已知信息派生的.至于性能,如果它是真正的常量,编译器应该在编译时用常量替换它. (8认同)
  • 我是逃跑的原始开发人员,如果可以运行,请不要更改它! (2认同)

oua*_*uah 15

你的程序中调用C.要修改不确定的行为ij两个控制顺序点之间超过一次.

在Java和JavaScript中,行为是明确定义的,您必须查看运算符的优先级和关联性.

  • 这取决于语言.它在Java中定义得很好. (12认同)
  • 这肯定是一个javascript问题.我们做这样的代码...有时:) (6认同)
  • 你没有看到其他标签?他们甚至更大:D (5认同)
  • @ouah要理解代码,优先级和关联性本身并不能削减它.还需要深入研究*lexer*以解析`(k +++ k)`,以及复合运算符的语义. (4认同)
  • @JonSkeet我从C标签来到这个问题,没有看到其他标签.我添加了对C的引用. (3认同)
  • @MarkoTopolnik - 这显然不是Java问题,因为我们不像这样编码:D (2认同)
  • @Andreas_D这是Java的一种特殊方言,叫做*pubescent*. (2认同)