当前位置: 首页 > 知识库问答 >
问题:

为什么"a^=b^=a^=b;"不同于"a^=b; b^=a; a^=b;"?

周苑博
2023-03-14

我尝试了一些代码在Java中交换两个整数,而不使用第三个变量,即使用XOR。

以下是我尝试的两个交换函数:

package lang.numeric;

public class SwapVarsDemo {

    public static void main(String[] args) {
        int a = 2984;
        int b = 87593;
        swapDemo1(a,b);
        swapDemo2(a,b);
    }

    private static void swapDemo1(int a, int b) {
        a^=b^=a^=b;
        System.out.println("After swap: "+a+","+b);
    }

    private static void swapDemo2(int a, int b) {
        a^=b;
        b^=a;
        a^=b;
        System.out.println("After swap: "+a+","+b);
    }

}

该代码产生的输出如下:

After swap: 0,2984
After swap: 87593,2984

我很想知道,为什么会有这样的说法:

        a^=b^=a^=b;

和这个不一样?

        a^=b;
        b^=a;
        a^=b;

共有3个答案

陈瀚
2023-03-14

这与Bloch和Gafter的Java拼图书中的一个条目非常相似,参见第2章,拼图7(“交换肉”)。我无法改进它。

解决方案中的解释是:

这个习惯用法在C编程语言中使用,并从那里进入C语言,但不能保证在这两种语言中都能工作。它保证不会在Java中工作。Java语言规范指出,运算符的操作数是从左到右计算的[JLS 15.7]。为了计算表达式x^=expr,在计算expr之前对x的值进行采样,并将这两个值的异或指定给变量x[JLS 15.26.2]。在CleverSwap程序中,对于表达式中的每个外观,变量x采样两次,但两次采样都发生在任何赋值之前。以下代码片段更详细地描述了中断交换习惯用法的行为,并解释了我们观察到的输出:

上述报价中引用的代码为:

// The actual behavior of x ^= y ^= x ^= y in Java
int tmp1 = x;     // First appearance of x in the expression
int tmp2 = y;     // First appearance of y
int tmp3 = x ^ y; // Compute x ^ y
x = tmp3;         // Last assignment: Store x ^ y in x
y = tmp2 ^ tmp3;  // 2nd assignment: Store original x value in y
x = tmp1 ^ y;     // First assignment: Store 0 in x
易京
2023-03-14

因为a^=b^=a^=b 的解析方式如下:

a ^= (b ^= (a ^= b));

可简化为:

a ^= (b ^= (a ^ b));

因此b将具有值b^(a^b),最后a将是a^(b^(a^b)

壤驷鸿祯
2023-03-14

问题是评估的顺序:

见JLS第15.26节。2.

首先,对左侧操作数求值以生成变量。如果此计算突然完成,则赋值表达式也会因为相同的原因突然完成;不计算右侧操作数,也不进行赋值。

否则,将保存左侧操作数的值,然后计算右侧操作数。如果此计算突然完成,则赋值表达式由于相同的原因突然完成,并且不会发生赋值。

否则,左侧变量的保存值和右侧操作数的值将用于执行复合赋值运算符指示的二进制运算。如果此操作突然完成,则赋值表达式出于相同的原因突然完成,并且不会发生赋值

否则,二进制运算的结果被转换为左手变量的类型,进行值集转换(§5.1.13)到适当的标准值集(不是扩展指数值集),并且转换的结果被存储到变量中。

所以你的表达是:

a^=b^=a^=b

  1. 评估a

换句话说,你的表达式相当于下面的java代码:

    int a1 = a;
    int b2 = b;
    int a3 = a;
    a = a3 ^ b;
    b = b2 ^ a;
    a = a1 ^ b;

您可以从方法的反汇编版本中看到:

  private static void swapDemo1(int, int);
    Code:
       0: iload_0       
       1: iload_1       
       2: iload_0       
       3: iload_1       
       4: ixor          
       5: dup           
       6: istore_0      
       7: ixor          
       8: dup           
       9: istore_1      
      10: ixor          
      11: istore_0  

 类似资料:
  • 问题内容: 我尝试了一些代码,使用XOR在Java中交换两个整数而不使用第三个变量。 这是我尝试的两个交换函数: 这段代码产生的输出是这样的: 我很好奇,为什么这样说: 与这个不同吗? 问题答案: 问题是评估的顺序: 参见JLS第15.26.2节 首先,对左操作数求值以产生一个变量。 如果该评估突然完成,则赋值表达式由于相同的原因而突然完成;右边的操作数不会被评估,并且不会发生赋值。 否则,将保存

  • 问题内容: 今天,我发现了python语言一个有趣的“功能”,这让我感到非常悲伤。 那个怎么样?我以为两者是等同的!更糟糕的是,这是我调试时遇到的麻烦的代码 WTF!我的代码中包含列表和字典,并且想知道我到底怎么把dict的键附加到列表上而又没有调用.keys()。事实证明,这就是方法。 我认为这两个陈述是等效的。即使忽略这一点,我也可以理解将字符串追加到列表的方式(因为字符串只是字符数组),但是

  • 然而,今天我在处理一些代码时,意外地发现以下两个交换给出了不同的结果: 这让我难以置信。有人能给我解释一下这里发生了什么吗?

  • 问题内容: 这是我的第一个问题,我开始学习Python。之间有什么区别: 和 在下面的示例中编写时,它显示不同的结果。 和 问题答案: 在中,在将右侧的表达式赋给左侧之前对其求值。因此,它等效于: 在第二个示例中,运行时已更改的值。因此,结果是不同的。

  • 我正在阅读SICP的树递归,其中是通过线性递归计算的。 我们还可以制定一个迭代过程来计算斐波那契数。其思想是使用一对整数a和b,初始化为Fib(1)=1和Fib(0)=0,并重复应用同时变换 不难证明,在应用该变换n次后,a和b将分别等于Fib(n1)和Fib(n)。因此,我们可以使用该过程迭代计算斐波那契数 (由Emacs Lisp重写,代替Scheme) “设置a b=a和b=a,我很难把我的

  • 问题内容: 这是我必须弄清楚怎么可能的代码。我有一个线索,但我不知道该怎么做。我认为这与负数和正数有关,也可能与变量修饰符有关。我是一个初学者,我到处都看过解决方案,但是找不到可用的东西。 问题是:您需要声明和初始化两个变量。如果条件必须为真。 代码: 感谢您抽出宝贵的时间。 问题答案: 这对于基本类型是不可能的。您可以使用带框的整数来实现: 在和比较将使用未装箱的值1,而将比较引用,并会成功,因