무한 루프
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
무한 루프는 컴퓨터 프로그램에서 루프가 종료 조건을 충족하지 못하거나, 충족될 수 없는 조건을 가져 무한히 반복되는 현상을 의미한다. 이는 프로그램의 논리적 오류, 자료형 및 연산 오류, 외부 요인 등 다양한 원인으로 발생할 수 있으며, 의도적인 경우도 있다. 무한 루프는 시스템의 응답 불능을 초래하거나, 스핀락, 게임 루프, 이벤트 루프 등 특정 상황에서 사용되기도 한다. 무한 루프는 정지 문제와 관련되어 있으며, 검출 및 해결이 항상 용이하지 않다.
무한 루프는 컴퓨터 프로그램에서 루프가 종료 조건을 갖지 않거나,[4] 충족될 수 없는 조건을 갖거나, 루프가 다시 시작되도록 하여 무한정 반복되는 일련의 명령이다. 구형 운영체제에서 협동적 멀티태스킹을 사용했을 때는,[5] 무한 루프가 시스템 전체를 멈추게 하는 경우가 많았다. 하지만, 현재 널리 사용되는 선점형 멀티태스킹 모델에서는 무한 루프가 발생해도 프로그램이 사용 가능한 프로세서 시간을 모두 소모할 뿐, 사용자가 프로그램을 종료할 수 있다. 대기 루프 역시 "무한 루프"라고 불리기도 한다. 무한 루프는 컴퓨터가 멈추는 한 가지 원인이 되며, 그 외에도 쓰레싱, 교착 상태, 접근 위반 등이 있다.
2. 무한 루프의 개념 및 유형

반복(루핑)은 특정 조건이 충족될 때까지 일련의 명령을 반복하는 것이다. 무한 루프는 루프 자체의 특성 때문에 조건이 절대 충족되지 않을 때 발생한다.
다음의 루프는 자체적으로 종료되지 않는 예시이다.
```lua
birds = 1
fish = 2
while birds + fish > 1 do
birds = 3 - birds
fish = 3 - fish
end
```
위 코드에서 ''birds''는 1 또는 2로, ''fish''는 2 또는 1로 번갈아 값을 가진다. 따라서 `birds + fish > 1` 조건은 항상 참이 되므로, 외부 개입("플러그 뽑기")이 없는 한 루프는 중단되지 않는다.
멀티 스레드 프로그램에서는 일부 스레드가 무한 루프 내에서 실행되더라도, 전체 프로그램이 무한 루프에 갇히지는 않을 수 있다. 메인 스레드가 종료되면 프로세스의 모든 스레드가 강제로 중지되어 실행이 종료된다. 무한 루프 내부의 스레드는 "하우스키핑" 작업을 수행하거나 소켓/큐로부터의 입력을 기다리는 차단된 상태에 있다가, 입력을 받을 때마다 실행을 재개할 수 있다.
2. 1. 의도하지 않은 무한 루프
의도하지 않은 무한 루프는 프로그래밍 오류로 인해 발생한다. 이는 초보 프로그래머에게서 흔하게 발생하지만, 숙련된 프로그래머도 미묘한 원인으로 인해 실수를 할 수 있다.[7] 주어진 프로그램이 종료될지, 아니면 영원히 실행될지는 정지 문제와 관련이 있으며, 이는 결정 불가능성 문제이다.[8]
흔한 원인 중 하나는 연결 리스트나 트리와 같은 자료 구조에서 노드를 반복 처리할 때, 부적절하게 링크를 형성하여 '참조 루프'를 만드는 것이다. 이로 인해 자료 구조의 일부가 링이 되어 무한 루프를 유발할 수 있다.
다음은 변수 초기화 오류로 인한 무한 루프를 보여주는 Visual Basic 코드이다.
```vbnet
dim x as integer
do while x < 5
x = 1
x = x + 1
loop
```
위 코드에서 루프 안에서 `x`는 항상 1로 재설정되고 1이 더해져 2가 된다. 따라서 `x < 5` 조건은 항상 참이므로 루프가 종료되지 않는다. `x = 1`을 루프 밖으로 옮겨 초기값을 한 번만 설정하면 이 문제를 해결할 수 있다.
C에서는 `=` (할당) 연산자와 `==` (동등성 테스트) 연산자를 혼동하여 무한 루프가 발생하기도 한다. 부동소수점 연산의 특성 때문에도 무한 루프가 발생할 수 있는데, 컴퓨터는 2진법 부동소수점으로 수를 처리하므로 미세한 오차가 발생하여 특정 값과의 비교 조건이 항상 참이 될 수 있기 때문이다.
C 언어에서 `goto` 문을 사용하면 무한 루프가 쉽게 발생한다. `goto` 문은 지정된 레이블로 무조건 이동하므로, 루프 탈출 조건이 없으면 프로그램은 영원히 종료되지 않는다.
수치 해석에서도 오차가 허용 오차 내로 수렴하지 않으면 무한 루프가 발생할 수 있다.
앨더슨 루프는 종료 조건이 존재하지만 프로그래머의 오류로 인해 접근할 수 없는 무한 루프를 의미하는 속어이다.[12] 1996년 앨더슨이라는 프로그래머가 Microsoft Access에서 확인 또는 취소 버튼이 없는 모달 대화 상자를 만들어 프로그램이 멈춘 현상에서 유래했다.[13]
여러 요소가 상호 작용하여 무한 루프가 발생할 수도 있다. 예를 들어, 두 서버가 서로 이해할 수 없는 메시지를 주고받으며 오류 메시지를 무한히 반복하는 경우가 있다.
2. 1. 1. 논리 오류
종료 조건이 없거나 잘못 설정되면 무한 루프가 발생할 수 있다. 초보 프로그래머뿐만 아니라 숙련된 프로그래머도 이러한 실수를 할 수 있는데, 그 원인이 매우 미묘할 수 있기 때문이다.[7]
흔한 원인 중 하나는 연결 리스트나 트리와 같은 자료 구조에서 노드를 반복 처리할 때 발생한다. 부적절하게 형성된 링크는 자료 구조에 '참조 루프'를 생성하여 무한 루프를 유발할 수 있다.
Visual Basic 코드 예시를 통해 변수 초기화 오류로 인한 무한 루프 발생을 살펴보자.
```vbnet
dim x as integer
do while x < 5
x = 1
x = x + 1
loop
```
위 코드에서 `x`는 루프 안에서 항상 1로 재설정된 후 1이 더해져 2가 된다. 따라서 `x < 5` 조건은 항상 참이 되므로 루프는 종료되지 않는다. 이 문제는 `x = 1`을 루프 밖으로 옮겨 초기값을 한 번만 설정하면 해결할 수 있다.
다음은 C 코드에서 흔히 발생하는 실수이다.
```c
#include
int main(void)
{
int a = 0;
while (a < 10) {
printf("%d\n", a);
if (a = 5)
printf("a equals 5!\n");
a++;
}
return 0;
}
```
`if (a = 5)`에서 `=` (할당) 연산자가 `==` (동등성 테스트) 연산자와 혼동되었다. `a = 5`는 `a`에 5를 할당하므로, `a`는 10으로 증가할 수 없어 무한 루프가 발생한다.
부동소수점 연산의 특성으로 인해 발생하는 무한 루프도 있다.
```c
float x = 0.1;
while (x != 1.1) {
printf("x = %f\n", x);
x = x + 0.1;
}
```
0.1을 10번 더하면 1.1이 되어야 하지만, 컴퓨터는 2진법 부동소수점으로 처리하므로 미세한 오차가 발생하여 `x != 1.1` 조건은 항상 참이 된다. 이러한 문제는 부등호를 사용하여 해결할 수 있다.
C 언어에서 `goto` 문을 사용한 무한 루프 예시는 다음과 같다.
```c
#include
int main(void) {
unsigned int x = 0;
LoopEnter:
x += 1;
printf("%u\n", x);
goto LoopEnter;
return 0;
}
```
`goto LoopEnter;` 문은 무조건 `LoopEnter` 레이블로 이동하므로 루프는 종료되지 않는다. `return 0;` 문은 도달 불가능 코드가 된다.
수치 해석에서도 오차가 허용 오차 내로 수렴하지 않으면 무한 루프가 발생할 수 있다.
```c
unsigned int i;
for (i = 1; i > 0; i++)
{ /*loop code*/ }
```
`i`는 `unsigned int` 형이므로 최댓값에 도달한 후 1을 더하면 0이 되어 루프를 탈출하는 것처럼 보이지만, `unsigned int`의 표현 범위에 따라 무한 루프가 될 수 있다.
앨더슨 루프는 종료 조건이 존재하지만 프로그래머의 오류로 인해 접근할 수 없는 무한 루프를 의미하는 속어이다.[12] 1996년 앨더슨이라는 프로그래머가 Microsoft Access에서 확인 또는 취소 버튼이 없는 모달 대화 상자를 만들어 프로그램이 멈추는 현상에서 유래했다.[13]
다음은 앨더슨 루프의 C 유사 의사 코드 예시이다.
```c
int sum = 0;
int i;
while (true) {
printf("합계에 더할 숫자를 입력하거나 종료하려면 0을 입력하세요");
i = getUserInput();
if (i * 0) { // i에 0을 곱한 값은 항상 0 (FALSE)
sum += i; // sum은 변경되지 않음
}
if (sum > 100) {
break; // sum이 변경되지 않으므로 이 조건은 절대 만족하지 않음
}
}
2. 1. 2. 자료형 및 연산 오류
컴퓨터에서 변수가 예상치 못한 특성을 가질 때 무한 루프가 발생할 수 있다.
```c
float x = 0.1;
while (x != 1.1) {
printf("x = %f\n", x);
x = x + 0.1;
}
```
위 코드는 0.1을 10번 더하면 1.1이 되어야 하지만, 컴퓨터는 2진법 부동소수점으로 처리하기 때문에 미세한 차이로 인해 무한 루프가 발생한다. 이 문제는 x가 특정 값과 같을 때 멈추는 조건을 부등호로 변경하여 해결할 수 있다.[7]
| x = 0.10000000149011611938 |
| x = 0.20000000298023223877 |
| x = 0.30000001192092895508 |
| x = 0.40000000596046447754 |
| x = 0.50000000000000000000 |
| x = 0.60000002384185791016 |
| x = 0.70000004768371582031 |
| x = 0.80000007152557373047 |
| x = 0.90000009536743164062 |
| x = 1.00000011920928955078 |
| x = 1.10000014305114746094 |
| x = 1.20000016689300537109 |
| ... |
```c
double x = 0.1;
while (x != 1.1) {
printf("x = %f\n", x);
x += 0.1;
}
```
위의 while 문 루프는 10번 실행되는 시스템도 있지만, 종료되지 않는 시스템도 있을 수 있다. 이는 루프 지속 조건 `(x != 1.1)`이 두 부동 소수점 수의 엄밀한 일치를 테스트하기 때문이다. 많은 컴퓨터의 (2진) 부동 소수점 계산에서는 0.1을 정확하게 표현할 수 없고 오차가 있어, 11번 더한 값이 1.1과 정확히 일치하지 않을 수 있다.
부동 소수점 수를 사용할 때는 등식 테스트가 실패할 가능성이 있으므로, 부등식으로 테스트하는 것이 안전하다. 예를 들어, `x`가 `1.1`과 같은지 테스트하는 대신 `(x < 1.1)` 또는 `(x <= 1.0)`으로 테스트한다. 또는, 루프 횟수의 상한을 정하고, 정수형 루프 카운터를 사용하여 문제를 회피할 수도 있다.
```c
int i; /* 정수형 루프 인덱스 */
double x = 0.1;
for (i = 0; i < 10; i++) {
printf("x = %f\n", x);
x += 0.1;
}
```
연결 리스트와 같은 데이터 구조가 루프를 가질 때, 반복 실행으로 인해 무한 루프가 발생할 수도 있다. 데이터 구조의 루프 검출은 간단한 연습 문제로 알려져 있다. 예를 들어, 12, 99, 37의 3개 정수 값을 저장한 순환 리스트의 내용을 출력하면 "12, 99, 37, 12, 99, 37, ..."과 같이 무한히 출력된다.
```c
unsigned int i;
for (i = 1; i != 0; i++) {
/* 루프 코드 */
}
```
이 코드는 무한정 반복될 것처럼 보이지만, `i`의 값은 결국 `unsigned int`에 저장할 수 있는 최대 값에 도달하고, 그 값에 1을 더하면 0으로 감싸져 루프가 종료된다. `i`의 실제 제한은 시스템 및 사용된 컴파일러에 따라 달라진다. 임의 정밀도 산술을 사용하면 이 루프는 컴퓨터의 메모리가 더 이상 `i`를 저장할 수 없을 때까지 계속된다.
2. 1. 3. 외부 요인
무한 루프는 여러 요인이 상호 작용하면서 발생할 수도 있다. 예를 들어, 요청을 이해하지 못하면 항상 오류 메시지로 응답하는 서버를 생각해 보자. 서버 자체에서는 무한 루프가 발생할 가능성이 없더라도, 두 개의 서버(A와 B)로 구성된 시스템은 무한 루프에 빠질 수 있다. 만약 A가 B로부터 알 수 없는 유형의 메시지를 받으면, A는 B에게 오류 메시지로 응답한다. B가 이 오류 메시지를 이해하지 못하면, 자체 오류 메시지로 A에 응답한다. A가 B의 오류 메시지를 이해하지 못하면, 또 다른 오류 메시지를 보내는 식으로 무한히 반복될 수 있다.[4]이러한 상황의 일반적인 예로 이메일 루프가 있다. 응답 불가 사서함에서 메일을 받았지만 자동 응답 기능이 켜져 있는 경우가 이에 해당한다. 사용자가 응답 불가 사서함에 답장을 보내면, "이것은 응답 불가 사서함입니다"라는 응답이 발생한다. 이 응답은 사용자에게 전송되고, 사용자는 다시 응답 불가 사서함에 자동 답장을 보내는 과정이 반복된다.[4]
2. 2. 의도적인 무한 루프
프로그램 목적에 따라 의도적으로 무한 루프를 사용하기도 한다.이론적으로 계산 가능한 것은 무한 루프가 될 수 없어야 하지만, 현실의 프로그램은 이론과 다르게 때로는 무한 루프가 필요하다. 예를 들어, 워드 프로세서와 같은 소프트웨어는 사용자의 명시적인 지시가 있을 때까지 실행되어야 한다. 또한, 인터넷이나 데이터베이스와 같은 서버 프로그램은 요청을 계속해서 기다리고 서비스를 제공해야 하며, 운영 체제와 같은 하위 계층 시스템에서도 이러한 경우가 많다.
멀티 스레드 프로그램에서 일부 스레드는 전체 프로그램이 무한 루프에 갇히지 않고 무한 루프 내에서 실행될 수 있다. 메인 스레드가 종료되면 프로세스의 모든 스레드가 강제로 중지되어 모든 실행이 종료되고 프로세스/프로그램이 종료된다. 무한 루프 내부의 스레드는 "하우스키핑" 작업을 수행하거나 소켓/큐로부터의 입력을 기다리는 차단된 상태에 있다가 입력을 받을 때마다 실행을 재개할 수 있다.
다음은 다양한 프로그래밍 언어에서의 무한 루프 예시이다.
- C 언어:
```c
#include
int main()
{
for (;;) // 또는 while (1)
printf("무한 루프\n");
return 0;
}
```
`for (;;)` 형식은 ''C 프로그래밍 언어''에 등장하며, 종종 "forever"로 발음되기도 한다.[11]
- BASIC (1980년대):
```basic
10 PRINT "무한 루프"
20 GOTO 10
```
- MS-DOS 배치 파일:
```bat
:A
echo 무한 루프
goto :A
```
여기서 루프는 마지막 줄이 무조건 실행을 처음으로 되돌리기 때문에 매우 명확하다.
- 자바:
```java
while (true) {
System.out.println("무한 루프");
}
```
`while` 루프는 조건이 항상 참이므로 종료되지 않는다.
- Bourne Again Shell (Bash):
```bash
for ((;;)); do
echo "무한 루프"
done
```
- Rust:
```rust
loop {
println!("무한 루프");
}
2. 2. 1. 유휴 루프
이러한 동작이 의도된 경우는 몇 가지가 있다. 예를 들어, 카트리지 기반 게임 콘솔의 게임은 일반적으로 메인 루프에 종료 조건이 없다. 프로그램이 종료될 운영 체제가 없기 때문이다. 루프는 콘솔의 전원이 꺼질 때까지 실행된다.현대 인터랙티브 컴퓨터는 사용자의 입력이나 장치 활동을 지속적으로 모니터링해야 하므로, 어떤 기본적인 수준에서 장치가 꺼지거나 재설정될 때까지 계속되어야 하는 무한 유휴 루프가 있다. 예를 들어, 아폴로 유도 컴퓨터에서 이 외부 루프는 Exec 프로그램에 포함되어 있었으며,[6] 컴퓨터가 다른 작업이 전혀 없을 경우 단순히 "컴퓨터 활동" 표시등을 끄는 더미 작업을 실행하는 루프를 돌았다.
현대 컴퓨터는 또한 충돌 시 프로세서나 마더보드 회로 구동 클럭을 일반적으로 정지시키지 않는다. 대신 오류 조건으로 전환하여 운영자에게 메시지를 표시하고 (예: 블루 스크린 오브 데스), 사용자에게 계속하라는 프롬프트에 응답하거나 장치를 재설정할 때까지 무한 루프에 들어간다.
OS가 없는 임베디드 환경에서는 전원 투입 후에 호출되는 메인 루틴(C의 경우 `main()` 함수)이 호출 측에 제어를 반환할 필요가 없다. 만약 메인 루틴을 빠져나와 제어를 반환해 버리면, 이후에는 기기 제어가 불가능해지므로 의미가 없기 때문이다. 메인 루틴에는 무한 루프(메인 루프)를 기술하고, 그 안에서 기기의 상태를 확인하면서 그 상태에 따른 분기 처리를 실행하게 된다.[15] 후술할 오래된 게임 전용기에서의 게임 루프도 이 형태이다. 다만 메인 루프 이외에서 무한 루프를 만들면 기기가 응답 불능 상태가 되므로, 메인 루프 이외의 무한 루프는 만들지 않도록 주의해서 구현할 필요가 있다.
2. 2. 2. 이벤트 루프
마이크로소프트 윈도우나 macOS로 대표되는 GUI 환경에서, 응용 소프트웨어는 각각 "메인 루프"라고 불리는 이벤트 루프(메시지 루프)를 가지고 있다.[16][17] 애플리케이션은 사용자의 입력 조작 이벤트나 화면 다시 그리기 이벤트의 발생을 루프에서 기다리면서, 응답을 반환하거나 그리기 처리를 실행한다. 이 스타일을 이벤트 구동 프로그래밍이라고 부른다. 메인 루프는 무한 루프로 하지 않고, 메인 윈도우의 닫기 버튼이 눌리는 등 하여 발생하는 닫기 이벤트나 종료 이벤트를 루프의 탈출 조건으로 하여, 메인 루프를 빠져나와 뒷정리를 한 다음 애플리케이션을 종료하는 것이 일반적인 방식이다. OpenGL을 사용한 고전적인 애플리케이션 프레임워크 중 하나로 GLUT가 있는데, GLUT의 `glutMainLoop()`는 한 번 호출하면 호출원으로 돌아오지 않는 무한 루프이다.[18] GLUT 애플리케이션은 메인 윈도우의 닫기 버튼에 응답하여 종료하는 구조로 되어 있지만, 표준 C 라이브러리의 `exit()` 함수로 반 강제 종료하고, 리소스의 뒷정리는 OS에 맡기는 형태가 된다.iOS나 안드로이드와 같은 모바일 OS에서는 통상적인 수단으로 애플리케이션 종료의 개념이 없고, 종료 이벤트도 존재하지 않기 때문에, 메인 루프는 실질적으로 무한 루프이다. 모바일 OS 환경에서는, 애플리케이션을 종료하려면 태스크(프로세스)를 강제 종료할 필요가 있다. 여유 메모리가 부족하게 되었을 때 등, OS 측의 판단에 의해 강제 종료될 수도 있다.
2. 2. 3. 스핀락
스핀락은 여러 개의 스레드 또는 프로세스 간에 메모리나 파일과 같은 계산 자원을 공유할 때, 데이터 경합이나 경합 상태 문제를 일으키지 않도록 락 등을 사용하여 공유 자원에 대한 접근을 상호 배제할 때 사용된다.[1] 락 해제(언락)를 잊거나 락의 순서를 잘못 지정하는 등 로직 오류가 있으면 데드락이 발생한다.[1] 락을 획득할 때까지 대기하는 동안, 루프를 돌면서 어떤 플래그를 감시하면서 대기하는 기법이 스핀락인데, 타임아웃 설정 없이 락 획득을 대기할 경우, 데드락이 발생하면 무한 루프에 빠진다.[1]스핀락은 공유 자원을 보호하기 위해 동시 프로그래밍에서 사용되는 하위 수준의 동기화 메커니즘이다.[1] 스핀락은 잠금을 획득할 수 없을 때 스레드를 대기 상태로 만드는 전통적인 잠금과는 달리, 잠금을 사용할 수 있게 될 때까지 무한 루프에서 반복적으로 "스핀"한다.[1] 이 의도적인 무한 루프는 스레드가 잠금을 기다리는 시간을 최소화하고 뮤텍스와 같은 상위 수준의 동기화 메커니즘의 오버헤드를 피하기 위한 설계 방식이다.[1]
뮤텍스처럼, 슬립 및 시그널 알림을 사용하여 CPU 시간을 낭비하지 않고 효율적으로 대기하는 방법도 있지만, 타임아웃 설정 없이 데드락이 발생하면 대기에서 벗어날 수 없어 광의의 무한 루프에 빠진다.[1]
2. 2. 4. 게임 루프
컴퓨터 게임 프로그램은 1/60초와 같은 아주 짧은 프레임 내에서 플레이어의 입력 조작에 대한 응답을 처리하면서 화면을 반복해서 그리는 실시간 처리 방식의 프로그래밍 스타일이며, 메인 루프는 "게임 루프"라고 불린다[19]. 패밀리 컴퓨터와 같은 구형 게임 전용기에서는 OS 개념이 없어 전원을 켜면 게임 프로그램이 메모리에 로드되어 즉시 시작되는 방식이었다. 게임 루프는 무한 루프이기 때문에 게임을 종료하고 다시 시작하려면 전원을 끄고 다시 켜거나 리셋 버튼을 눌러야만 했다. 이후 게임 내용이 복잡해지고 리소스의 효율적인 관리와 이식성 향상을 위해 범용 PC에서 사용되는 OS의 서브셋이 게임 전용기에도 도입되었다. 게임기가 CD나 DVD와 같은 멀티미디어 재생 기능도 탑재하게 되었고, 인터넷에서 구매한 게임을 스토리지에 다운로드하여 설치한 후 실행할 수 있게 되면서, 게임을 종료하는 데 전원 버튼이나 리셋 버튼밖에 방법이 없다면 사용성이 좋지 않으므로, 프로그램 측에서 게임 루프를 종료하고 시스템 화면으로 돌아갈 수도 있게 되었다[20].3. 무한 루프의 예시 (프로그래밍 언어별)
다음은 다양한 프로그래밍 언어에서 무한 루프를 구현하는 예시들이다.
- C/C++:
```c
#include
int main()
{
for (;;) // 또는 while (1)
printf("무한 루프\n");
return 0;
}
```
`for (;;)` 형식은 무한 루프를 만드는 전통적인 방식이며, ''C 프로그래밍 언어''에 등장한다.[11]
- Java:
```java
while (true) {
System.out.println("무한 루프");
}
```
`while` 루프의 조건이 항상 참이므로 무한 루프가 된다.
- BASIC:
```basic
10 PRINT "무한 루프"
20 GOTO 10
```
- MS-DOS 배치 파일:
```bat
:A
echo 무한 루프
goto :A
```
마지막 줄에서 무조건 처음으로 돌아가기 때문에 무한 루프가 된다.
- Bash:
```bash
for ((;;)); do
echo "무한 루프"
done
```
- Rust:
```rust
loop {
println!("무한 루프");
}
```
- Visual Basic:
```vbnet
dim x as integer
do while x < 5
x = 1
x = x + 1
loop
```
`x`는 루프 안에서 항상 1로 재설정된 후 1이 더해지므로, `x`는 항상 2가 된다. 따라서 `x < 5` 조건은 항상 참이 되어 무한 루프가 발생한다. 이 문제는 `x = 1`을 루프 밖으로 옮겨서 해결할 수 있다.
- 부동 소수점 연산 오류로 인한 무한 루프 (C/C++, Python):
```c
float x = 0.1;
while (x != 1.1) {
printf("x = %f\n", x);
x = x + 0.1;
}
```
```python
x = 0.1
while x != 1:
print(x)
x += 0.1
```
컴퓨터는 부동소수점 방식으로 실수를 표현하므로, 0.1을 정확하게 표현할 수 없다. 따라서 `x`에 0.1을 더하는 과정에서 발생하는 미세한 오차 때문에 `x`는 정확히 1.1 (또는 Python에서는 1)이 되지 않아 무한 루프가 발생한다.
| AMD Turion 프로세서에서의 C 출력: |
|---|
이러한 문제는 부동소수점 비교 연산의 부정확성 때문에 발생한다. 해결 방법은 `x != 1.1` 대신 `x < 1.1` 또는 `x <= 1.0`과 같이 부등식을 사용하거나, 정수형 루프 인덱스를 사용하는 것이다.
- C 언어에서 unsigned int 사용 시 주의점:
```c
unsigned int i;
for (i = 1; i != 0; i++) {
/* 루프 코드 */
}
```
`i`는 unsigned int 형이기 때문에, 최댓값에 도달한 후 1을 더하면 0으로 순환한다. 따라서 `i != 0` 조건은 항상 참이 되어, 무한 루프가 발생할 것이라고 생각할 수 있지만, 0으로 돌아가 루프를 탈출한다.
- 의도하지 않은 할당 연산자 사용 (C):
```c
#include
int main(void)
{
int a = 0;
while (a < 10) {
printf("%d\n", a);
if (a = 5)
printf("a equals 5!\n");
a++;
}
return 0;
}
```
`if (a = 5)`에서 `=`는 할당 연산자이므로, `a`에 5가 할당된다. `a`는 10으로 진행할 수 없으며, 이 루프는 종료될 수 없다. `==`(동등 비교) 연산자를 사용해야 한다.
- C 언어에서 goto 문의 사용:
```c
#include
int main(void) {
unsigned int x = 0;
LoopEnter:
x += 1;
printf("%u\n", x);
goto LoopEnter;
return 0;
}
```
`goto LoopEnter;` 문은 무조건 `LoopEnter` 레이블로 이동하므로 무한 루프가 발생한다. `return 0;` 문은 도달 불가능 코드가 된다.
4. 무한 루프의 검출 및 해결
무한 루프는 코드를 자세히 검사하여 찾아낼 수 있지만, 모든 경우에 해당하는 것은 아니다. 예를 들어, 다음과 같은 간단한 프로그램이 있다.
# 양의 정수 n을 입력받는다.
# n이 짝수이면 2로 나눈다.
# n이 홀수이면 3을 곱하고 1을 더한다.
# n이 1이면 종료한다.
# 2단계로 돌아간다.
이 프로그램은 매우 간단하지만, n이 27일 때 9232까지 커지는 등 예측하기 어렵다. 이 프로그램이 어떤 n에 대해서도 종료되는지, 아니면 무한 루프에 빠지는 n이 존재하는지는 콜라츠 문제라고 불리며, 2014년 시점에서 미해결 문제이다.
겉으로는 무한 루프로 보이지만 예외나 전역 점프 (C 언어에서는 `setjmp()` 함수와 `longjmp()` 함수), 루프 밖에서 주어진 계속 호출을 통해 탈출하는 패턴도 있다. 서버처럼 무한히 처리해야 하는 경우가 아니라면, 일반적인 처리 흐름에 예외 처리를 사용하는 것은 좋지 않다. 예를 들어, 파일의 종료를 알리기 위해 예외를 사용하는 것은 부적절하다.
자르곤 파일에 수록된 "The Story of Mel"에는 무한 루프로 보이지만, 오버플로우를 이용해 인접 메모리를 덮어씀으로써 점프 명령을 자기 수정하여 종료하는 기술이 소개되어 있다.[22]
무한 루프는 종종 프로세스에 신호를 보내거나(유닉스에서 SIGINT), 프로세서에 인터럽트를 보내 중단시킬 수 있다. 작업 관리자나 Control-C 명령, kill 명령을 사용할 수 있다. 그러나 프로세스가 응답하지 않거나 프로세서가 인터럽트 불가능 상태일 때는 작동하지 않을 수 있다. SIGKILL 같은 신호가 효과가 있을 수 있지만, 시스템 종료 없이는 루프를 종료할 수 없는 경우도 있다.
`while (true)` 루프는 break 문이나 return 문으로 탈출할 수 있어 무한 루프가 아닐 수 있다.
PHP 예시:
```php
while (true) {
if ($foo->bar()) {
return;
}
}
```
다음은 무한 루프의 예시이다.
```vb
dim x as integer
do until x > 5
x = 1
x = x + 1
loop
```
위의 코드에서 `x = 1`이 루프 안에 있어 x는 항상 2가 되므로, 조건을 만족하지 못해 무한 루프가 된다. `x = 1`을 루프 밖으로 옮기면 해결된다.
다음은 변수가 예상치 못한 성질을 가져 무한 루프가 되는 경우다.
```c
float x = 0.1;
while (x != 1.1) {
printf("x = %f\n", x);
x = x + 0.1;
}
```
0.1을 10번 더하면 1.1이 되어야 하지만, 컴퓨터는 부동소수점으로 처리하므로 미세한 오차가 생겨 무한 루프가 발생한다. 이 문제는 조건을 부등호로 변경하여 해결할 수 있다.
4. 1. 멈춤 문제 (정지 문제)
실수를 하여 무한 루프가 생길 수 있다. 무한 루프가 생기는지를 확인하는 일반적인 방법은 존재하지 않는다(정지 문제).[4] 대부분의 무한 루프는 코드를 자세히 검사하여 찾을 수 있지만, 주어진 프로그램이 언젠가는 종료될지 또는 영원히 실행될지를 결정하는 일반적인 방법은 없다. 이는 결정 불가능성인 정지 문제와 관련이 있다.[8]이론상, 프로그램이 정지하는지 계속 작동하는지를 "어떤 프로그램에 대해서도", "유한 시간 내에", "반드시 결정할 수 있다"는 것을 모두 만족하는 방법은 없다. 이것은 정지 문제의 결정 불가능성으로부터의 결론이다.
4. 2. 무한 재귀
무한 재귀는 재귀에 의해 발생하는 무한 루프의 특수한 경우이다.다음은 VBA의 예로, 스택 오버플로우 오류를 반환한다.
```vbscript
Sub Test1()
Call Test1
End Sub
```
무한 재귀는 재귀에서 발생하는 무한 루프의 특수한 경우이다. 가장 간단한 예로는, 의사 언어로 표현된 람다 계산의 Ω항이 있다.
```ada
procedure Omega () is
procedure omega_ (p : procedure) is
begin
p(p) ;
end ;
begin
omega_(omega_) ;
end ;
```
Ω는 무한 재귀이므로, 정규형을 갖지 않는다. 기저 사례가 없거나, 귀납 단계가 불완전한 구조적 재귀에서는 보통 무한 재귀가 발생한다.
이러한 불완전한 구조적 재귀를 다음 코드로 예시한다. `sumFrom1To_` 함수는 무한 재귀가 된다. 이를 수정하려면 기저 사례를 추가해야 한다.
```ada
function sumFrom1To_ (n : Integer) return Integer is
begin
return n + sumFrom1To_ (n-1) ;
end ;
```
이 문제를 수정한 것이 다음 함수이다. 이 함수는 `n`이 `1` 미만이거나, `n`이 너무 클 때만 무한 재귀가 되며, 첫 번째 경우는 오류 검사를 통해 피할 수 있다.
```ada
function sumFrom1To (n : Integer) return Integer is
begin
if n = 1 Then
return 1 ;
else
return n + sumFrom1To(sub1 n) ;
end if ;
end ;
```
실제 컴퓨터 시스템에서는 서브루틴의 재귀 호출 종료 조건이 없는 등의 이유로 무한 재귀가 발생하면, 호출 스택을 소모하여 스택 오버플로우를 일으키고, 프로그램이 비정상적으로 종료될 수 있다.
5. 특수한 경우
여러 프로그램이 상호작용하면서 무한 루프가 발생할 수 있다. 예를 들어, A 서버가 B 서버로부터 알 수 없는 메시지를 받으면 오류 메시지를 보내고, B 서버도 이 오류 메시지를 이해하지 못해 다시 A 서버에게 오류 메시지를 보내는 상황이 반복될 수 있다. 이러한 현상의 대표적인 예시로 메일 루프가 있다.[21]
메일 루프는 자동 응답 기능이 켜져 있는 응답 불가 사서함에 메일을 보냈을 때 발생한다. 사용자가 응답 불가 사서함에 답장을 보내면, "이것은 응답 불가 사서함입니다"라는 자동 응답이 오고, 사용자는 다시 자동 응답을 받는 과정이 무한히 반복된다.
가상 무한 루프(pseudo-infinite loop)는 실제로는 매우 긴 루프이지만, 무한 루프처럼 보이는 것을 의미한다.
앨더슨 루프(Alderson loop)는 종료 조건이 존재하지만, 프로그래머의 실수로 인해 코드 구현에서 접근할 수 없는 무한 루프를 뜻하는 전문 용어이다.[13] 1996년, 한 프로그래머(성: 앨더슨)가 Microsoft Access에서 확인 또는 취소 버튼이 없는 모달 대화 상자를 만들어 프로그램 전체를 비활성화시킨 사건에서 유래했다.[12][21]
C-유사 의사 코드로 표현된 앨더슨 루프의 예시는 다음과 같다.
```text
int sum = 0;
int i;
while (true) {
printf("합계에 더할 숫자를 입력하거나 종료하려면 0을 입력하세요");
i = getUserInput();
if (i * 0) { // i에 0을 곱한 값은 항상 0 (거짓)이므로 이 조건은 항상 거짓이다.
sum += i; // 따라서 sum은 절대 변경되지 않는다.
}
if (sum > 100) { // sum이 절대 증가하지 않으므로 이 조건은 절대 참이 될 수 없다.
break; // 따라서 루프는 절대 종료되지 않는다.
}
}
6. 무한 루프와 관련된 기타 내용
현실의 프로그램은 이론과 달리 버그 등으로 인해 종료되지 않는 무한 루프가 발생할 수 있다. 표 계산 소프트웨어나 워드 프로세서처럼 사용자의 지시가 있을 때까지 실행되어야 하는 프로그램, 인터넷이나 데이터베이스 서버처럼 요청을 계속 처리해야 하는 프로그램, 운영 체제처럼 항상 작동해야 하는 시스템에서는 무한 루프가 필요하기도 하다. 그러나 "무한 루프"라는 용어는 보통 의도하지 않은 오류로 인해 강제 종료 등으로만 멈출 수 있는 상황을 가리킨다.[23]
여러 개의 스레드나 프로세스가 메모리 등을 공유할 때 락을 사용하여 상호 배제를 해야 한다. 락 해제를 잊거나 순서를 잘못 지정하면 데드락이 발생한다. 스핀락은 락 획득을 위해 루프를 돌며 플래그를 감시하는데, 타임아웃 설정이 없으면 데드락 발생 시 무한 루프에 빠진다. 뮤텍스처럼 CPU 시간을 낭비하지 않고 효율적으로 대기하는 방법도 있지만, 타임아웃 없이는 데드락에서 벗어날 수 없어 넓은 의미의 무한 루프에 빠진다.
"무한 루프"는 메모리 누수나 데드락처럼 비 프로그래머에게도 널리 쓰이며, 프로그래밍 오류 외의 상황에도 사용된다. 예를 들어, 컴퓨터 사용 절차가 반복되어 제자리로 돌아오거나, 목적 달성이나 회피 수단이 모두 없는 경우[23], 자발적인 반복[24] 등에 사용된다.
미국 캘리포니아주 쿠퍼티노에는 Infinite Loop라는 주소가 있다. 애플 본사 애플 파크 내 타원형 도로 이름이 "Infinite Loop"이며, 주변에 동심원 모양 주차장이 있다. Infinite Loop 안쪽 6개 건물은 1 Infinite Loop부터 6 Infinite Loop까지의 주소를 가지며, 애플 본사 주소는 1 Infinite Loop, Cupertino, California이다.
슈퍼컴퓨터 성능을 소재로 한 농담도 있다. 자르곤 파일에는 "Cray-3는 너무 빨라서 무한 루프를 2초 안에 실행할 수 있다!"[25]라는 농담이 있다.
참조
[1]
웹사이트
Endless loop dictionary definition
https://www.yourdict[...]
2020-01-22
[2]
웹사이트
What is infinite loop (endless loop)
https://whatis.techt[...]
2020-01-22
[3]
뉴스
Overload of Hangers-On Creates Bumpy Ride for Internet Stocks
https://archive.nyti[...]
1999-08-16
[4]
간행물
Codes and Modes: The Character of Documentary Culture
http://www.flowjourn[...]
2020-01-23
[5]
간행물
Non-preemptive Multitasking
https://www.pcmag.co[...]
2024-02-07
[6]
웹사이트
The History of Apollo On-board Guidance, Navigation, and Control
http://klabs.org/his[...]
Charles Stark Draper Laboratory
2020-01-23
[7]
웹사이트
New York Times Crossword Answers
https://nyxcrossword[...]
2020-01-22
[8]
웹사이트
Halting Problem in Theory of Computation
https://www.geeksfor[...]
2020-01-22
[9]
웹사이트
A Buffer Overflow Exploit Against the DameWare Remote Control software
https://pen-testing.[...]
2020-01-22
[10]
문서
Ada Programming: Control: Endless Loop
https://en.wikibooks[...]
[11]
웹사이트
Endless loop in C/C++
https://stackoverflo[...]
[12]
웹사이트
Alderson loop
https://www.lee-dohm[...]
2020-01-22
[13]
웹사이트
Alderson Loop
http://www.catb.org/[...]
2006-05-21
[14]
웹사이트
無限ループの英訳|英辞郎 on the WEB
https://eow.alc.co.j[...]
[15]
웹사이트
組み込みソフトウェア工学 - 第6回 組み込みプログラム例題 | 神奈川工科大学
http://www.ess.ic.ka[...]
[16]
웹사이트
WM_CLOSE message (Winuser.h) - Win32 apps | Microsoft Learn
https://learn.micros[...]
[17]
웹사이트
WM_QUIT message (Winuser.h) - Win32 apps | Microsoft Learn
https://learn.micros[...]
[18]
웹사이트
3.1 glutMainLoop
https://www.opengl.o[...]
[19]
웹사이트
ゼロからはじめるXNAプログラミング - 更新と描画 | マイナビニュース
https://news.mynavi.[...]
[20]
웹사이트
ゼロからはじめるXNAプログラミング - ゲームの起動とウィンドウ制御 | マイナビニュース
https://news.mynavi.[...]
[21]
웹사이트
Alderson Loop
http://www.catb.org/[...]
[22]
웹사이트
The Story of Mel
http://www.catb.org/[...]
[23]
웹사이트
Caught in an infinite loop (無限ループにハマりました)
https://web.archive.[...]
[24]
웹사이트
Confession of an infinite looper (無限ルーパーの告白)
https://web.archive.[...]
[25]
웹사이트
infinite loop
http://www.catb.org/[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com