博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
谜题7:互换内容
阅读量:5010 次
发布时间:2019-06-12

本文共 1553 字,大约阅读时间需要 5 分钟。

下面的程序使用了复合的异或赋值操作符,它所展示的技术是一种编程习俗。那么它会打印出什么呢?

public class CleverSwap{    public static void main(String[] args){ int x = 1984; // (0x7c0) int y = 2001; // (0x7d1) x^= y^= x^= y; System.out.println("x= " + x + "; y= " + y); } }

就像其名称所暗示的,这个程序应该交换变量x和y的值。如果你运行它,就会发现很悲惨,它失败了,打印的是

x = 0; y = 1984。

交换两个变量的最显而易见的方式是使用一个临时变量:

int tmp = x;x = y;y = tmp;

很久以前,当中央处理器只有少数寄存器时,人们发现可以通过利用异或操作符(^)的属性(x ^ y ^ x) == y来避免使用临时变量:

x = x ^ y;y = y ^ x;x = y ^ x;

这个惯用法曾经在C编程语言中被使用过,并进一步被构建到了C++中,但是它并不保证在二者中都可以正确运行。但是有一点是肯定的,那就是它在Java中肯定是不能正确运行的。

Java语言规范描述到:操作符的操作数是从左向右求值的。为了求表达式 x ^= expr的值,x的值是在计算expr之前被提取的,并且这两个值的异或结果被赋给变量x。在CleverSwap程序中,变量x的值被提取了两次——每次在表达式中出现时都提取一次——但是两次提取都发生在所有的赋值操作之前。

下面的代码段详细地描述了将互换惯用法分解开之后的行为,并且解释了为什么产生的是我们所看到的输出:

// Java中x^= y^= x^= y的实际行为int tmp1 = x ; // x在表达式中第一次出现int tmp2 = y ; // y的第一次出现 int tmp3 = x ^ y ; // 计算x ^ y x = tmp3 ; // 最后一个赋值:存储x ^ y 到 x y = tmp2 ^ tmp3 ; // 第二个赋值:存储最初的x值到y中 x = tmp1 ^ y ; // 第一个赋值:存储0到x中

在C和C++中,并没有指定表达式的计算顺序。当编译表达式x ^= expr时,许多C和C++编译器都是在计算expr之后才提取x的值的,这就使得上述的惯用法可以正常运转。尽管它可以正常运转,但是它仍然违背了C/C++有关不能在两个连续的序列点之间重复修改变量的规则。因此,这个惯用法的行为在C和C++中也没有明确定义。

为了看重其价值,我们还是可以写出不用临时变量就可以互换两个变量内容的Java表达式的。但是它同样是丑陋而无用的:

// 杀鸡用牛刀的做法,千万不要这么做!

y = (x^= (y^= x))^ y ;

这个教训很简单:在单个的表达式中不要对相同的变量赋值两次。表达式如果包含对相同变量的多次赋值,就会引起混乱,并且很少能够执行你希望的操作。即使对多个变量进行赋值也很容易出错。更一般地讲,要避免所谓聪明的编程技巧。它们都是易于产生bug的,很难以维护,并且运行速度经常是比它们所替代掉的简单直观的代码要慢。

语言设计者可能会考虑禁止在一个表达式中对相同的变量多次赋值,但是在一般的情况下,强制执行这条禁令会因为别名机制的存在而显得很不灵活。例如,请考虑表达式 x = a[i]++ - a[j]++,它是否递增了相同的变量两次呢?这取决于在表达式被计算时i和j的值,并且编译器通常是无法确定这一点。

转载于:https://www.cnblogs.com/yuyu666/p/9840350.html

你可能感兴趣的文章
【iOS】UICollectionView自己定义Layout之蜂窝布局
查看>>
【以太坊钱包开发 一】MyEtherWallet 钱包开发项目概述
查看>>
golang——(strings包)常用字符串操作函数
查看>>
发布aar到jcenter
查看>>
跨浏览器问题的五种解决方案
查看>>
XPath定位时,使用文本的方法小技巧。
查看>>
安装pandas报错(AttributeError: 'module' object has no attribute 'main')
查看>>
ch02 fundamental definition 01
查看>>
JSON解析
查看>>
Position is everything?(css定位学习的一些心得)(一)
查看>>
如何提高编程水平
查看>>
Jquery Uploadify3.21.与2.1版本 使用中存在的问题--记录三
查看>>
Linux查看进程的内存占用情况 分类: ubuntu ...
查看>>
[BZOJ 2818]Gcd
查看>>
FORM值传递与地址传递
查看>>
(译)yaml快速教程
查看>>
C:大数相加
查看>>
160. Intersection of Two Linked Lists
查看>>
人生苦短,我用python-- Day11
查看>>
JAVA Bean
查看>>