不用臨時變量交換兩個數的值

原文轉自:點擊打開鏈接
當要交換兩個數的值時,通常的做法是定義一個臨時變量,然後再進行交換。那麼能不能不用臨時變量而交換兩個數的值呢?可以的!C語言提供的異或運算就可以實現這樣的操作。
 
異或運算符^也稱XOR運算符,它的規則是若參加運算的兩個二進位同號,則結果爲0(假);異號爲1(真)。即0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0。
 
例:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int a = 2, b = 6;

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

    printf("a = %d b = %d/n", a, b);

    return 0;
}

結果如下:

a = 6 b = 2

分析:

前兩個賦值語句:“a = a ^ b;”和“b = b ^ a;”相當於b = b ^ (a ^ b),而b ^ a ^ b等於a ^ b ^ b。b ^ b的結果爲0,因爲同一個數與相向相^,結果必爲0。因此b的值等於a ^ 0,即a,其值爲2。

再執行第三個賦值語句:“a = a ^ b”。由於a的值等於(a ^ b),b的值等於(b ^ a ^ b),因此,相當於a = a ^ b ^ b ^ a ^ b,即a的值等於a ^ a ^ b ^ b ^ b,等於b。

2007.06.02
 
今天又發現另外兩種方法,特補上。
 
方法一

void swap(int *p, int *q)
{
    *= *+ *q;
    *= *- *q;
    *= *- *q;
}

方法二

void swap(int *p, int *q)
{
    *= *+ *- (*= *p);
}

原理爲算術運算符的結合順序爲自左至右。

2007.06.18
 
可以把異或的三條語句壓縮爲一條:
 

a ^= b ^= a ^= b;

要交換三個變量的值,可以用以下語句:

= a + c - (= b, b = c);

2007.09.11

謝謝網友cool8jay指出上面紅色部分的錯誤,下面來分析一下,先看一個示例程序,如下:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int a = 1;
    int b = 2;
    int c = 3;

    c = a + c - (= b, b = c);

    printf("a = %d/n", a);
    printf("b = %d/n", b);
    printf("c = %d/n", c);

    return 0;
}

編譯運行,結果如下:

a = 2 b = 3 c = 2

可見,最初目標中的c = 1部分沒能實現。於是分析原因:

《The C Programming Language·Second Edition》P205介紹說,“The additive operators + and - group left-to-right”,即附加運算符加號和減號的結合順序是自左向右;P209介紹說,“A pair of expressions separated by a comma is evaluated left-to-right, and the value of the left expression is discarded. The type and value of the result are the type and value of the right operand”,即逗號表達式的計算順序是從左向右,逗號左邊表達式的值被捨棄,表達式結果的類型和數值是逗號右邊表達式的類型和數值。

既然如此,那結果應該是正確的,爲什麼還會出錯呢?再來看一下VC6.0下編譯產生的彙編代碼:

7: int a = 1;
00401028 mov dword ptr [ebp-4],1
8: int b = 2;
0040102F mov dword ptr [ebp-8],2
9: int c = 3;
00401036 mov dword ptr [ebp-0Ch],3
10:
11: c = a + c - (= b, b = c);
0040103D mov eax,dword ptr [ebp-8]
00401040 mov dword ptr [ebp-4],eax
00401043 mov ecx,dword ptr [ebp-4]
00401046 add ecx,dword ptr [ebp-0Ch]
00401049 mov edx,dword ptr [ebp-0Ch]
0040104C mov dword ptr [ebp-8],edx
0040104F sub ecx,dword ptr [ebp-8]
00401052 mov dword ptr [ebp-0Ch],ecx

可以看到,第11行代碼的執行過程如下:

先把b的值賦給a,再把a和c的值相加,結果放在寄存器ecx中,然後把c的值賦給b,最後再把ecx與b的值相減,結果賦給c。分解爲C語句爲:

= b; /* a = 2, b = 2 */
ecx = a + c; /* ecx = 5, a = 2, c = 3 */
= c; /* b = 3, c = 3 */
= ecx - b; /* c = 2, ecx = 5, b = 3 */

這下就清楚了,原來程序運行時先把逗號左邊表達式的值計算出來丟棄掉,然後再做其它的運算,最開始的錯誤也就錯在了這裏。

好了,既然上面的式子有問題,那有沒有其它正確的方法能一條語句實現三個變量的值的交換呢?

 

不用臨時變量順次交換N個變量的值
 
 
有N(N>=2)個變量,不使用臨時變量,如何順次交換它們的值?能否只用一條語句實現?如
 
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 |
+---+---+---+---+---+
 
要把它變爲
 
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
| 2 | 3 | 4 | 5 | 1 |
+---+---+---+---+---+
 
怎樣實現?
 
首先,我們考慮用異或實現兩個變量的交換,可參考我的這篇文章《不用臨時變量交換兩個數的值》。用C++寫函數如下:
 

int &swap(int &a, int &b)
{
    b = b ^ a;
    a = a ^ b;
    b = b ^ a;

    return b;
}

然後可以把代碼優化爲:

int &swap(int &a, int &b)
{
    b ^= a;
    a ^= b;
    b ^= a;

    return b;
}

繼續優化,把三句壓縮爲一句,如下:

int &swap(int &a, int &b)
{
    b ^= a ^= b ^= a;
    
    return b;
}

還可再優化,如下:

int &swap(int &a, int &b)
{
    return (b ^= a ^= b ^= a);
}

現在來順次交換5個變量的值,如下:

swap(a, b);        //返回b

swap(b, c);        //返回c

swap(c, d);        //返回d

swap(d, e);        

既然有返回值,那麼可以寫成鏈式的,如下:

swap(a, b);                                 //返回b

swap(swap(a, b), c);                        //返回c

swap(swap(swap(a, b), c), d);               //返回d

swap(swap(swap(swap(a, b), c), d), e);        

現在,讓我們來把swap函數依次用相應的函數體替換掉,如下:

e ^= d ^= e ^= swap(swap(swap(a, b), c), d);

e ^= d ^= e ^= d ^= c ^= d ^= swap(swap(a, b), c);

e ^= d ^= e ^= d ^= c ^= d ^= c ^= b ^= c ^= swap(a, b);

e ^= d ^= e ^= d ^= c ^= d ^= c ^= b ^= c ^= b ^= a ^= b ^= a;

好了,最後一個語句就實現了順次交換五個變量的值,寫程序驗證如下:

/******************************************************************
* Copyright (c) 2005-2007 CUG-CS
* All rights reserved
*
* 文件名稱:xor.c
* 簡要描述:實現五個變量的順次交換
*
* 當前版本:1.0
* 作 者:raincatss
* 完成日期:2007-10-18
* 開發環境:Windows XP Sp2 + VC6.0
* 個人博客:http://raincatss.cublog.cn/
******************************************************************/


#include <stdio.h>

#define PRINT(A) do {/
    printf("%d/n", A);/
    } while (0)

int main()
{
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;
    int e = 5;

//    b ^= a ^= b ^= a;        // swap(a, b)

//    c ^= b ^= c ^= b;        // swap(b, c)

//    d ^= c ^= d ^= c;        // swap(c, d)

//    e ^= d ^= e ^= d;        // swap(d, e)


    e ^= d ^= e ^= d ^= c ^= d ^= c ^= b ^= c ^= b ^= a ^= b ^= a;

    PRINT(a);
    PRINT(b);
    PRINT(c);
    PRINT(d);
    PRINT(e);

    return 0;
}

運行結果如下:

2 3 4 5 1

測試結果正確,達到預定目標。

知道了五個變量的順次交換,那麼任意多個變量的順次交換就可以依次推導出來,在此我就不多寫了。

當然,除了本文介紹的方法外,還有其它方法可以實現不用臨時變量交換任意多個變量的值,讀者可以自己探索一下。

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