C、C++、Java異或運算交換變量變量值的區別

今天看到一位大神的博客,深受感觸。決定也發一篇博客,證明一下我還活着。
於是我翻看以前學習時做的一些筆記,整理了一下,得到了一個關於異或運算交換變量變量值的筆記。

首先來看下面三組表達式,看起來他們都能實現交換兩個變量的值。

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

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

a ^= b ^= a ^= b;

可實際的情況是,前面2組表達式,在C、C++、Java中都能順利完成變量值的交換。而第3組表達式,卻只在C、C++中通過了,而在Java中卻得到了意料之外的結果。請看下面的截圖


在C、C++中得到了想要的結果


而在Java中,卻得到了這樣的結果

怎麼樣,是不是很驚訝,在java中,a的值,換給了b,但不管怎麼做,a的值都是0,怎麼會這樣?百思不得其解。這事就此擱了下來。

過了很長時間之後,在意個偶然的機會中,我在一個關於Java謎題的手冊中看到了這個問題,原來這還是Java比較經典的謎題之一了。

原來,事情是這樣的。

很久以前,當中央處理器只有少數寄存器時,人們發現可以通過利用異或操作符(^)的屬性(x ^ y ^ x) == y來避免使用臨時變量,這個慣用法曾經在C編程語言中被使用過,並進一步被融入到了C++中,但是它並不保證都可以正確運行。但是有一點可以肯定:它在Java中肯定是不能正確運行的。 

Java語言規範描述到:操作符的操作數是從左向右求值的。爲了求表達式 x ^= expr的值,x的值是在計算expr之前被提取的,並且這兩個值的異或結果被賦給變量x。在OprDemo程序中,變量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中 

從上面的代碼可以看出,其實a之所以會爲0,是因爲a^a造成的,我們知道,兩個相同的值異或其值爲0.

在C和C++中,並沒有指定表達式的計算順序。當運行表達式x^=expr時,許多C和C++編譯器是在計算expr之後才提取x的值的,這使得上述的做法可以得到正確的結果。

那麼在Java中,有沒有辦法使得不使用中間變量的單個表達式來達到這個目的呢?這是可以的,請看下面的代碼。
y = (x^= (y^= x))^ y ; 
這句代碼就能夠做到
寫這麼多,最後想說的就是在單個的表達式中不要對同一變量賦值兩次,賦值次數多了,就會引起混亂。

發佈了35 篇原創文章 · 獲贊 5 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章