史萊姆論壇

史萊姆論壇 (http://forum.slime.com.tw/)
-   程式語言討論區 (http://forum.slime.com.tw/f76.html)
-   -   請問一下在別版看到的一個有趣寫法?? (http://forum.slime.com.tw/thread221927.html)

a19870504 2007-12-11 02:13 PM

請問一下在別版看到的一個有趣寫法??
 
我在微風C++版看到有人提出 兩個變數進行數值交換時

可以使用XOR的方式來交換 印象中好像是 a^= b^= a ^= b

反正就是利用XOR的特性直接進行數值交換

但是有某大提出效率的問題 他說這種方法效率會比多設一個變數還要少一半

且他也有自己用程式進行了一億次的試驗證明這個說法

只是很好奇說利用XOR的特性來寫 程式看起來相當的簡單整潔

為什麼效率會比多設一個變數來交換還要慢??

mini 2007-12-11 03:19 PM

這個人也蠻好奇的
所以做了以下實驗

先寫一段
語法:

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    int a=100,b=99;
   
    a^= b^= a ^= b;
    /* 寫的平民化一點就是
        a=a^b;
    b=a^b;
    a=a^b; */
   
    system("PAUSE");
    return EXIT_SUCCESS;
}

編譯後這裡用 OllyDbg 觀察
因為 99的16進制值 是 63
search一下就找到以下程式碼
語法:

MOV DWORD PTR SS:[EBP-4],64              ; |
MOV DWORD PTR SS:[EBP-8],63              ; |
MOV EDX,DWORD PTR SS:[EBP-8]            ; |
LEA EAX,DWORD PTR SS:[EBP-4]            ; |
XOR DWORD PTR DS:[EAX],EDX              ; |
MOV EDX,DWORD PTR SS:[EBP-4]            ; |
LEA EAX,DWORD PTR SS:[EBP-8]            ; |
XOR DWORD PTR DS:[EAX],EDX              ; |
MOV EDX,DWORD PTR SS:[EBP-8]            ; |
LEA EAX,DWORD PTR SS:[EBP-4]            ; |
XOR DWORD PTR DS:[EAX],EDX              ; |
MOV DWORD PTR SS:[ESP],專案1.00440000      ; |ASCII "PAUSE"
CALL <JMP.&msvcrt.system>                ; \system

乍看之下好像 沒有被做什麼多餘的加工

接著再來比較用暫存變數的方式
a^= b^= a ^= b;
改成
tmp=a; a=b; b=tmp;

同樣觀察編組譯後的組合語言碼
語法:

MOV DWORD PTR SS:[EBP-4],64              ; |
MOV DWORD PTR SS:[EBP-8],63              ; |
MOV EAX,DWORD PTR SS:[EBP-4]            ; |
MOV DWORD PTR SS:[EBP-C],EAX            ; |
MOV EAX,DWORD PTR SS:[EBP-8]            ; |
MOV DWORD PTR SS:[EBP-4],EAX            ; |
MOV EAX,DWORD PTR SS:[EBP-C]            ; |
MOV DWORD PTR SS:[EBP-8],EAX            ; |
MOV DWORD PTR SS:[ESP],專案1.00440000      ; |ASCII "PAUSE"
CALL <JMP.&msvcrt.system>                ; \system

同樣也是好像沒被做什麼多餘的加工的樣子


但仔細一看
原來還是編譯器在作祟
用暫存變數的方式之程式碼只花了 10行
而用互斥或方式之程式碼花了 13行
多出了 3行 LEA 機械碼
至於為何 互斥或方式 要用到 LEA呢 ?

LEA 指令:
語法:

LEA 是 80x86 CPU 指令集的一員,在 8086 CPU 就已經有這個指令,這個指令是用來取得變數的位址,其語法是:

LEA 暫存器,變數名
這個指令和 OFFSET 假指令的功能幾乎一樣,但是它是在 CPU 執行時,才取得變數的位址;而 OFFSET 則是在組譯時就取得位址。因此 LEA 可以用在變數位址可能會改變的情形,例如取得區域變數的位址。

使用 xor 編譯器會用到 堆疊節區 與 資料節區 (及暫存器)
可看出
堆疊節區 是用來做運算暫存的
資料節區 用來擺放結果

使用 mov 編譯器 只會用到 堆疊節區 (及暫存器)
資料在 堆疊節區 及 暫存器 之間移動

那一定要這麼做嗎? (xor)
這當然有它(編譯器)的理由
只是別人(編譯器)都這麼做了
除非你要 改寫編譯器 或 事後改寫機械碼(組合語言)
否則問這一句
沒有多大意義

當然 c/c++ 是很活的語言
你也可以自己寫 嵌入式組合語言在 c/c++裡
這樣編譯器就拿你沒則了
不過
在編譯後 有可能反而會再多出幾行 機械碼也不一定
(畢竟c/c++的嵌入式組合語言 編譯器 可不是照單全收的)


所有時間均為台北時間。現在的時間是 08:35 AM

Powered by vBulletin® 版本 3.6.8
版權所有 ©2000 - 2024, Jelsoft Enterprises Ltd.

『服務條款』

* 有問題不知道該怎麼解決嗎?請聯絡本站的系統管理員 *


SEO by vBSEO 3.6.1