Java随笔:使用异或操作交换变量值的风险

在面试中,经常会问到“如何不用中间变量交换两个变量值”。

看看下面这个代码输出是什么:

int x = 1984;
int y = 2001;
x^=y^=x^=y;
System.out.println("x="+x+";y="+y);

看上去应该很完美的:x=2001;y=1984

实际输出是:x=0;y=1984

问题出在哪里?是的,就是JVM的编译器。

看看实际的汇编:

   Code:
      0: sipush        1984
      3: istore_1
      4: sipush        2001
      7: istore_2
      8: iload_1
      9: iload_2
     10: iload_1
     11: iload_2
     12: ixor
     13: dup
     14: istore_1
     15: ixor
     16: dup
     17: istore_2
     18: ixor
     19: istore_1
     20: getstatic     #2

这里并没有按照我们想象中的顺序:

  1. x^=y
  2. y^=x^=y
  3. x^=y^=x^=y

而是这样的顺序:

  1. x=1984
  2. y=2001
  3. temp1=x
  4. temp2=y
  5. temp3=x^y
  6. x=temp3
  7. y=temp2^temp3
  8. x=temp1^y

让我们为汇编对应进行一下注释:

   Code:
      0: sipush        1984
      3: istore_1 // x=1984
      4: sipush        2001
      7: istore_2 // y=2001
      8: iload_1 // 堆栈(高到低) 1984
      9: iload_2 // 2001, 1984
     10: iload_1 // 1984,2001,1984
     11: iload_2 // 2001,1984,2001,1984
     12: ixor // 2001^1984 ,堆栈(17,2001,1984)
     13: dup // temp3=2001^1984=17
     14: istore_1 // x=temp3=17
     15: ixor // 17^2001,堆栈(1984,,1984)
     16: dup // temp3=1984
     17: istore_2 // y=1984
     18: ixor  // 1984^1984,堆栈(0)
     19: istore_1 // x=0
     20: getstatic     #2

也就是说,实际编译器没有按照我们想象的那样,执行x^=y^=x^=y。

总结

单个表达式不要对相同的变量赋值2次,否则无法从混乱中得到预期的结果。

使用异或进行数值交换,容易产生bug,不易读且难以维护。

时间: 2024-12-22 08:26:12

Java随笔:使用异或操作交换变量值的风险的相关文章

python 学习7 交换变量值

在其他语言中,交换两个变量值的时候,可以这样写: temp = a a = b b = temp 在Python中,我们可以简单的这样写: a,b=b,a 实验如下: >>> a=10 >>> b=5 >>> print(a,b) 10 5 >>> a,b=b,a >>> print(a,b) 5 10 >>>

Java反射-修改private final成员变量值,你知道多少?

大家都知道使用java反射可以在运行时动态改变对象的行为,甚至是private final的成员变量,但并不是所有情况下,都可以修改成员变量.今天就举几个小例子说明.  基本数据类型 String类型 Integer类型 总结 首先看下对基本类型的修改: /** * @author Cool-Coding 2018/5/15 */ public class ReflectionUsage {private final int age=18; public int getAge(){ return

C++ 实参和形参 交换变量值

1. 传值调用机制 ( call- by-value machanism ) (1). 在形参位置插入的是实参的值.如果实参是变量,则插入的只是变量的值,而非变量本身. (2). 传值调用形参是局部变量.调用函数时,该函数的形参被初始化为实参的值. eg: void swap (int x, int y) { int temp; temp = x; x = y; y = temp; } main() { int a = 1, b = 2; cout << "a = " &l

C语言交换变量值的几种方法

第一种:使用中间变量 int a = 1, b = 2, c; c = a; a = b; b = c; printf(“%d,%d”, a, b); 第二种: int a = 1, b = 2; a = a + b; b = a - b; a = a - b; printf(“%d%d”, a, b); 或者: a = a * b; b = a / b; a = a / b; 第三种: int a = 1, b = 2; a ^= b; b ^= a; a ^= b; printf(“%d,%

java并发-记一次统计变量值偏差问题

1 问题描述 在一个项目中,需要对发送的请求结果进行统计,开发同事定义了两个全局共享变量CommonUtil.ReqFailNum和ReqNum,分别记录请求失败数和发送的请求数.并在每次发送请求之前都假定该请求会处理失败,先对其累加,直到成功收到200的返回码后,重新修正失败数量. 最后当应用处理请求处于较频繁的阶段时,出现了ReqFailNum最后减为负数的情况,一次正常请求完成时,CommonUtil.ReqFailNum ++;和CommonUtil.ReqFailNum --应该是成对

JavaScript交换两个变量值的七种解决方案

前言 这篇文章总结了七种办法来交换a和b的变量值 1 2 var a = 123; var b = 456; 交换变量值方案一 最最最简单的办法就是使用一个临时变量了,不过使用临时变量的方法实在是太low了 1 2 3 4 var t; t = a; a = b; b = t; 首先把a的值存储到临时变量中,然后b赋值给a,最后拿出临时变量中的a值赋给b,这个办法是最基本的了 交换变量值方案二 下面的方案都不会有临时变量,我总结了一下,其实不使用临时变量的思路都是让其中一个变量变成一个a和b都有

如何不利用一个额外的变量来达到交换两个变量值的目的-------位上的异或运算

问题:一般我们要交换两个变量的值,多会采取一个额外变量来实现,比如temp=a,a=b,b=temp,现在我们能不利用temp来实现交换a,b两个变量值的目的吗? 解决方案:^异或运算符,而且我们会发现a^a=0,还有0^a=a,依据这两条理论,我们可以实现之前提出的问题. 代码如下: #include<stdio.h>void inplace_swap(int * x,int * y){    *y=*x^*y;    *x=*x^*y;    *y=*y^*x;}int main(){  

C#交换两个变量值的多种写法

在学习.Net/C#或者任何一门面向对象语言的初期,大家都写过交换两个变量值,通常是通过临时变量来实现.本篇使用多种方式实现两个变量值的交换. 假设int x =1; int y = 2;现在交换两个变量的值. 使用临时变量实现 static void Main(string[] args) { int x = 1; int y = 2; Console.WriteLine("x={0},y={1}",x, y); int temp = x; x = y; y = temp; Cons

用异或操作实现的交换函数用以实现数组逆置中须要注意的问题

用元素交换函数实现数组逆置非常easy,如以下代码:(数组左右元素交换) #include<iostream> #include<stdlib.h> using namespace std; void swap(int &a, int &b) { int tmp = a; a = b; b = tmp; } int main() { int a[5] = { 1, 2, 3, 4, 5 }; int lenth = sizeof(a) / sizeof(a[0]);