無窮迴圈

無窮迴圈（endless loop）又稱無窮迴圈、無限迴圈（infinite loop），是指程式的控制流程一直在重複執行某一段程式碼，無法結束的情形，其原因可能是因為程式中的迴圈沒有設結束迴圈條件，或是結束迴圈的條件不可能成立等。在協作式多工（cooperative multitasking）的作業系統中，無窮迴圈會使系統沒有反應，若是先佔式多工（preemptive multitasking）的系統中，無窮迴圈會用掉所有可用的處理器時間，不過可以由使用者結束程式。

有些無窮迴圈是因為程式錯誤而意外產生的，不過也有些程式的架構是以無窮迴圈為基礎，在迴圈執行過程中處理各種事件，這種無窮迴圈是正常的，不是程式錯誤。

無窮迴圈是造成系統假宕機的原因之一，其他的可能原因包括死鎖或是記憶體區段錯誤。

忙碌等待迴圈是在外界特定條件時（例如有按鍵輸入）才會離開的迴圈，有時忙碌等待迴圈也被稱為是無窮迴圈，但此情形和上述的不太一樣。忙碌等待迴圈可以藉由外界事件而結束迴圈，但上述的無窮迴圈是無法結束的。

蓄意或無意產生的無窮迴圈

迴圈是指程式可能會重複執行的某一組指令，若重複執行，在特定條件成立後才會結束，若因為迴圈的特性，造成特定條件無法滿足，就會形成無窮迴圈。

1.1 蓄意產生的無窮迴圈

有些情形下程式會蓄意產生無窮迴圈。例如早期使用 ROM 匣的遊戲，在主程式的迴圈是無窮迴圈，沒有結束條件，原因是一般主程式若不執行時，系統控制權是交由作業系統，但這類遊戲沒有作業系統，因此讓主程式為無窮迴圈，程式會一直執行到斷電為止。

現在許多的電腦互動系統需要定期的監控使用者的輸入或是裝置的活動，因此當系統閒置時，仍然會在無窮迴圈中執行，直到系統被重置或斷電為止。像在阿波羅計劃導航用的電腦中，程式的最外層是無窮迴圈，若完全沒有其他工作要進行時，電腦會關閉「電腦運作中」的指示燈號。

1.2 無意產生的無窮迴圈

若程式中的無窮迴圈是無意產生的，也就是程式錯誤。新手程式設計師常常會出現這種錯誤，但在資深程式設計師身上也有可能發生，而且錯誤的原因可能相當微妙，因此不容易除錯。

例如程式設計師想要從收集連結串列中所有的資料，因此會依連結串列依序前進，直到連結串列的尾端為止，但若程式未考慮連結串列可能是迴圈連結串列，在迴圈連結串列中依序前進的程式，反而會移動到連結串列較前面的部份，此時就會變成無窮迴圈。

大部份的無窮迴圈可以藉由詳細的的程式碼檢視而找出，不過沒有通用的方式可以判斷程式是否會結束，或者會永遠執行（也就是無窮迴圈）。判斷程式會結束或會永遠執行的問題稱為停機問題，而英國電腦科學家艾倫・圖靈證明了沒有停機問題的通用演演算法。

舉例

以下是一些無窮迴圈的例子。

C 語言的無窮迴圈：

include

main

{while (1) {printf ("Infinite Loop\n");// 顯示 "Infinite Loop" 字串

}

}

上述程式會一直顯示 "Infinite Loop" 字串。

BASIC 語言的無窮迴圈：

10 PRINT "Infinite Loop"

20 GOTO 10' 跳到行號 = 10 的位置

X86 組合語言的例子：

loop:

; 空的無窮迴圈，直接跳到 "loop" 標籤的位置

jmp loop

Python 的例子：

while True:

print ("Infinite Loop")# 顯示 "Infinite Loop" 字串

2.1 邏輯錯誤

以下是一個 Visual Basic 無窮迴圈的例子：

dim x as integer

do until x > 5 ' 根本不會有 x>5 的情形

x = 1

x = x + 1

loop

每一次執行迴圈時 x 會先設定為 1，然後變為 2，因為數值未大於 5，所以永遠不會結束。若將 x = 1 由迴圈內部移到迴圈之前即可以改善此一情形。

有些程式設計師可能因為不熟悉特定程式語言的語法而造成無窮迴圈，例如以下是一段 C 語言的程式：

include

main

{

int a = 0;

while (a

其預期輸出是數字 0 至 9，其中 5 和 6 中間會顯示 "a equals 5!"，但程式設計師在編寫程式時將設定用的 = 運運算元及判斷相同的 == 運運算元弄混了，因此程式會在每次執行迴圈時都會將 a 設定為 5，因此變數 a 永遠無法到達 10，此迴圈就變成了無窮迴圈。

現代化的編譯器，例如 clang，都已經可以檢測到這一類的潛在問題並給出警告。

2.2 變數處理錯誤

有時不適當的迴圈結束條件也可能會造成無預期的無窮迴圈，例如以下 C 語言的例子：

float x = 0.1;

while (x != 1.1) {// 可能會因為浮點運算的誤差而出現問題

printf ("x = % f\n", x);

x = x + 0.1;

}

在有些作業系統中，上述程式會執行 10 次迴圈然後結束，但有些系統中，上述程式卻可能會一直執行，無法結束，問題主要在迴圈的結束條件 (x != 1.1) 要在二個浮點數相等時才結束，結果會依系統處理浮點數的方式而定，只要系統執行 10 次迴圈後的結果和 1.1 差一點點，上述程式就會變成無窮迴圈。

若將結束條件改為 (x 就沒有這個問題，程式可能會多執行一次迴圈，但不會變成無窮迴圈。另一種解決方式則是用一個整數變數作為迴圈變數，再依此變數判斷是否要結束迴圈。

在數值分析程式中也可能會出現無預期的無窮迴圈，例如程式需一直迭代到誤差小於某特定值為止，但若因為運算中的捨去誤差，使得誤差一直無法小於該特定值，就會產生無窮迴圈。

2.3 奧爾德森迴圈

奧爾德森迴圈（Alderson loop）是指一個迴圈有設定結束條件，但因為程式的寫法（多半是程式設計錯誤），造成永遠無法滿足結束條件，在針對使用者介面程式偵錯時最容易出現這類的問題。

以下 C 的虛擬碼中有一個奧爾德森迴圈，程式是要計算使用者輸入一串數字的和，使用者輸入 0 時結束迴圈，但程式中用了不正確的運算子：

sum = 0;

while (true) {printf ("Input a number to add to the sum or 0 to quit");

i = getUserInput ;

if (i * 0) { // 若 i 乘 0 為真，則使 sum 加上 i 的值

sum += i; // 但這不可能發生，因為不論 i 為何值（i 0）都是 0。如果條件中用的是！= 而非 ，程式碼就能正常執行

}

if (sum> 100) {break; // 終止迴圈。結束條件存在，但從來沒有達到過，因為 sum 永遠不會增加}

}

「奧爾德森迴圈」來自一個 Microsoft Access 的程式設計師，他編寫的程式產生一個有模式的對話方塊，使用者需要回應，程式才能繼續運作，但對話方塊沒有 OK 鍵或取消鍵，因此只要此對話窗出現，Access 程式就無法繼續運作。

2.4 無窮遞迴

無窮遞迴是一種由遞迴造成的無窮迴圈。例如以下計算階乘的 C 語言程式：

unsigned int fac (unsigned int a)

{//n!=n*(n-1)!

return (fac (a-1) * a);

}

一般遞迴的程式會有一特定條件，此條件成立時直接計算結果，而不是透過遞迴來計算結果，若程式中未定義此條件，就會出現無窮遞迴。

無窮遞迴會造成堆疊溢位，而無窮遞迴不會結束，因此也是無窮迴圈的一種。不過若遞迴程式是使用尾端遞迴的處理方式，在有些程式語言（如 Scheme）中會最佳化成迴圈，因此不會造成堆疊溢位。

上述的程式可以修改成沒有無窮遞迴的程式。

unsigned int fac (unsigned int a)

{if (a == 0) {// 定義 0!=1

return 1;

}

else {return ( fac (a-1) * a);

}

}

多個模組產生的無窮迴圈

無窮迴圈也可能因為多個模組之間的互動而產生。考慮一臺伺服器若收到無法理解的需求時，會回應錯誤訊息，此架構中不會有無窮迴圈。但若有二臺上述的伺服器（A 和 B），互相交換資料，A 收到由 B 所送出無法理解的需求，會回應錯誤訊息給 B，但若 B 也無法理解 A 送出的需求（其實是 A 的錯誤訊息），會再以自己的格式回應錯誤訊息給，A 收到後無法理解，會再回應錯誤訊息給 B……。像就是這類的例子。

假無窮迴圈

假無窮迴圈是指一個迴圈看似不會結束，但只是一個執行很長時間，最後仍會結束的迴圈。

以下是一個 C 語言 for 迴圈的程式：

unsigned int i;

for (i = 1; i != 0; i++) {/ loop code /}

上述程式每次執行時都將 i 加 1，若 i 等於 0 時才會結束迴圈，此程式看似不會結束，但最後還是會結束。程式中型態為 unsigned int 的變數，其數值有一定上限，當數值已到上限，再加 1 時，變數數值就會變為 0，因此讓程式結束。實際的上限值依系統及編譯器而不同，假如 unsigned int 是一個 16 個位元的字元組，上述的迴圈會執行 65536 次。若使用高精度計算，程式會一直執行到記憶體無法儲存 i 為止。