前兩天寫了這學期 Foundamentals of Data Structures 課裡最後一個 Project 的程序。題目的難點本來在於思路,可是老師上課已經提醒了很多,於是編程上並沒有太大困難。布置下來以後我花了兩個小時的時間,把整個程序寫完,通過了 PAT 上的測試,把程序交給小組的測試員,又和文檔員說了一下這個題的思路,覺得自己已經 Mission Acomplish 了。
昨天測試員回復我說有一個數據通不過,程序陷入了死循環。我的第一反應就是她給的數據有問題。因為這妹子懶得寫程序,數據竟然是手算出來的。我看到 N = 50 覺得她肯定出錯了。不過為了放心,還是不耐煩地自己寫了一個數據生成器,對比之後發現,程序真的錯了。
具體的算法不再詳述,思路上都沒有問題。bug 出在一個循環數組上。把每一步結果打印出來後,發現程序卡在這個地方不停循環:
for (j = k; j != i; ++j) {
if (j == n)
j -= n;
/* do something... */
}
你可以先不要看我接下來的分析,自己看一下這段代碼有什麼問題,不是很難。
我把這個循環體內部的 i 和 j 變量全部打印出來以後,發現即使 j == i 成立,循環也不會停止。想了好久,才發現,這樣的寫法是有問題的。如果你還沒有看出來,我把它展成下面的 while 語句:
j = k;
while (j != i) {
if (j == n)
j -= n;
/* do something... */
j++;
}
這段程序會在 i == 0 的時候陷入死循環。若在循環語句開始時 j == n 成立,在執行操作的時候 j 會變成 0,而在循環體末尾,j 又做了自增操作,因此判斷語句中 j != i 永遠成立。
這個問題不難,但有些隱晦,如果當初這樣寫,那麼發現錯誤可能就比較容易了:
for (j = k; j != i; ++j) {
/* do something... */
if (j == n)
j -= n;
}
這樣在執行操作的時候 j 的值就等於 n,可能會出現數組越界、訪問無效內存等等錯誤,就比較好排查了。
這個問題改起來也比較容易,只需要把 for 語句中的最後一句寫得丑一點就行了:
for (j = k; j != i; j = (j + 1) % n) {
/* do something... */
}