昨天我見過以下功能。交換兩個變量內容的另一種方式是什麼?
static void swap (int *p, int *q) {
*p -= *q;
*q += *p;
*p = *q - *p;
}
在哪種情況下該功能不起作用?我認爲可能存在溢出問題,而且效率不高。還有別的事嗎?
昨天我見過以下功能。交換兩個變量內容的另一種方式是什麼?
static void swap (int *p, int *q) {
*p -= *q;
*q += *p;
*p = *q - *p;
}
在哪種情況下該功能不起作用?我認爲可能存在溢出問題,而且效率不高。還有別的事嗎?
溢出問題不是一個「可能」,它確實存在:當您以較大的正數交換高等級的負數時,會發生溢出,這是根據標準的未定義行爲。
但最重要的是,出於可讀性的原因,使用這個(或異或技巧)代替通常的交換(通過臨時變量)絕對沒有意義。
+1爲可讀性註釋 – Paul
'0x7FFFFFFFE'需要35位,您在初始化時已經溢出。如果初始化'p'和'q'的數字不需要比'int'的寬度更多的位,那麼在溢出時你的行爲沒有定義,但是如果行爲恰好是環繞的,它就會起作用。 –
@DanielFischer你說得很對,我應該更仔細地計算一下我的位數。非常感謝您的糾正! – dasblinkenlight
其實上面的答案是錯誤的。對於p,它得到0xFFFFFFFE,因爲它用0x7FFFFFFFE初始化q,它不適合,並且(除了未定義的行爲)事實上被截斷爲0xFFFFFFFE。
因此,該答案中的代碼實際上以p = 0xFFFFFFFF和q = 0xFFFFFFFE開頭,併成功交換了它們。
(編輯:這是錯誤的,它已現予以更正)
其實日常工作完全(除了最後的未定義行爲,我不知道是否有或沒有,我有點模糊關於標準的細節)。
忽略最終未定義的行爲,如果底層算法是常見的2的補碼模算術,則該例程正常工作,並且在溢出/下溢(它是模算術)時沒有問題。
唯一真正的問題是鋸齒。如果兩個指針相同,則指向的整數將變爲0.
因此例程的前提條件是兩個指針是有效的而不是別名。檢查空指針和相同的指針可能會被添加,如果這是你期望你的程序需要。
爲什麼使用這種機制而不是xor技巧而不是temp變量swap的問題仍然存在。但是與OP問題沒有關係:例程工作和溢出,因爲它是由2的補碼模算術處理的,這是處理器上最常見的,這不是問題(編輯:它只是因爲溢出未定義的行爲該標準,但它適用於使用2的補碼模算術的任何處理器)。至於效率,這一切都取決於優化器和處理器。
已簽名整數的溢出是未定義的行爲,它在大多數系統上工作使其更加隱蔽,因爲人們可能會越來越期望它能夠工作。 (這個錯誤早已得到糾正,所以你的回答在這方面已經過時了。) –
我確實寫了「忽略最終未定義的行爲」。你說對了,你可能會越來越期待它(未定義的行爲意味着任何事情都可能發生,包括按照預期工作),但重點是處理器算法使得該算法有效。儘管所有的機器都使用模算術,這使得這個問題值得懷疑,但事實上溢出在C中是未定義的行爲。至於這個錯誤已經得到糾正......這不是我寫作的時候。我是一個緩慢的typer,對不起。 –
溢出的評論是因爲你寫了「我不確定是否存在」,只是爲了澄清,而不是批評。至於緩慢的打字,我可以同情,我也很慢。關於「儘管所有的機器都使用模算術」,但這不是什麼好事,但不幸的是,還有一些補充機器。 –
也許XOR技巧?注意:p和q *可以指向同一個對象。 – wildplasser
是的,這些恰恰是問題,溢出和別名。 –
謝謝,我沒有想到別名的情況。 – md5