史萊姆論壇

返回   史萊姆論壇 > 專業主討論區 > 程式語言討論區
忘記密碼?
論壇說明

歡迎您來到『史萊姆論壇』 ^___^

您目前正以訪客的身份瀏覽本論壇,訪客所擁有的權限將受到限制,您可以瀏覽本論壇大部份的版區與文章,但您將無法參與任何討論或是使用私人訊息與其他會員交流。若您希望擁有完整的使用權限,請註冊成為我們的一份子,註冊的程序十分簡單、快速,而且最重要的是--註冊是完全免費的!

請點擊這裡:『註冊成為我們的一份子!』

Google 提供的廣告


發文 回覆
 
主題工具 顯示模式
舊 2007-12-11, 02:13 PM   #1
a19870504
註冊會員
榮譽勳章

勳章總數
UID - 283728
在線等級: 級別:2 | 在線時長:15小時 | 升級還需:6小時級別:2 | 在線時長:15小時 | 升級還需:6小時
註冊日期: 2007-10-25
文章: 34
精華: 0
現金: 56 金幣
資產: 56 金幣
預設 疑問 - 請問一下在別版看到的一個有趣寫法??

我在微風C++版看到有人提出 兩個變數進行數值交換時

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

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

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

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

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

為什麼效率會比多設一個變數來交換還要慢??
a19870504 目前離線  
送花文章: 0, 收花文章: 6 篇, 收花: 14 次
回覆時引用此帖
舊 2007-12-11, 03:19 PM   #2 (permalink)
管理版主
 
mini 的頭像
榮譽勳章
UID - 4144
在線等級: 級別:97 | 在線時長:9889小時 | 升級還需:107小時級別:97 | 在線時長:9889小時 | 升級還需:107小時級別:97 | 在線時長:9889小時 | 升級還需:107小時級別:97 | 在線時長:9889小時 | 升級還需:107小時級別:97 | 在線時長:9889小時 | 升級還需:107小時級別:97 | 在線時長:9889小時 | 升級還需:107小時級別:97 | 在線時長:9889小時 | 升級還需:107小時
註冊日期: 2002-12-07
文章: 13354
精華: 0
現金: 26473 金幣
資產: 3024383 金幣
預設

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

先寫一段
語法:
#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++的嵌入式組合語言 編譯器 可不是照單全收的)

此帖於 2007-12-11 08:54 PM 被 mini 編輯. 原因: 打錯...
mini 目前離線  
送花文章: 2015, 收花文章: 8007 篇, 收花: 26812 次
回覆時引用此帖
有 2 位會員向 mini 送花:
Admin1 (2007-12-11),rank (2008-02-24)
感謝您發表一篇好文章
發文 回覆



發表規則
不可以發文
不可以回覆主題
不可以上傳附加檔案
不可以編輯您的文章

論壇啟用 BB 語法
論壇啟用 表情符號
論壇啟用 [IMG] 語法
論壇禁用 HTML 語法
Trackbacks are 禁用
Pingbacks are 禁用
Refbacks are 禁用

相似的主題
主題 主題作者 討論區 回覆 最後發表
請問一下關於照片做成vcd的一個問題 hyc56721 一般電腦疑難討論區 2 2003-07-15 10:33 AM


所有時間均為台北時間。現在的時間是 12:00 AM


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


SEO by vBSEO 3.6.1