while 루프는 조건이 참으로 남아 있는 동안 본문을 반복합니다. 잔액이 목표에 도달하거나, 큐가 비거나, 입력이 떨어질 때까지처럼 필요한 반복 횟수를 미리 모를 때 쓰세요.
출력:
Year 1: 1070.00
Year 2: 1144.90
Year 3: 1225.04
Year 4: 1310.80
Year 5: 1402.55
Year 6: 1500.73
Goal reached after 6 years
조건은 첫 번째를 포함해 매 반복 전에 검사됩니다. savings가 이미 목표에 도달했다면 본문은 한 번도 실행되지 않고 프로그램은 "after 0 years"를 출력합니다. 루프는 본문이 조건에서 읽는 무언가를 바꾸는 것에 의존합니다. 여기서는 savings < goal이 거짓이 될 때까지 savings가 해마다 늘어납니다.
do while: 최소 한 번 실행
do while 루프는 검사를 끝으로 옮기므로 본문이 항상 최소 한 번 실행됩니다:
출력:
Attempt 1: got 7
Attempt 2: got 4
Attempt 3: got 1
Done after 3 attempts
code의 첫 값은 본문이 한 번 실행된 뒤에야 존재하며, 바로 이런 때 do while이 어울립니다. 사람들이 자주 걸리는 문법 세부 사항이 두 가지 있습니다:
while (code != 1)뒤의 세미콜론은 필수입니다. 빠뜨리면 컴파일 오류입니다.- 조건에서 쓰는 변수는
do앞에서 선언해야 합니다. 중괄호 안에서 선언한 변수는 조건이 실행될 때 이미 범위를 벗어나므로do { int code = ...; } while (code != 1);은 컴파일되지 않습니다.
같은 루프를 while로 쓰면 루프 전에 첫 호출을 중복하거나, 첫 실행을 강제하는 임시 시작 값이 필요합니다. do while은 "시도하고, 다시 시도할지 결정한다"를 그대로 표현합니다.
종료 값까지 입력 읽기
종료 단어가 나오거나 입력이 끝날 때까지 줄을 읽는 것은 교과서적인 while 루프입니다. Console.ReadLine()은 더 이상 입력이 없으면 null을 반환하므로 둘 다 검사하세요:
출력:
skipping "abc": not a number
3 amounts, total 49.50
(line = Console.ReadLine()) != null은 다음 줄을 대입하고 검사하는 것을 한 식에서 합니다. !=가 =보다 강하게 결합하므로 대입을 감싼 괄호가 필요합니다. 루프는 done에서 멈추므로 그 뒤의 99는 읽히지 않습니다. decimal.TryParse는 예외 없이 abc를 거부하고, continue는 다음 줄로 건너뜁니다. null 검사가 없으면 입력 끝에 도달했을 때 ReadLine이 계속 null을 반환하므로 TryParse에 영원히 null을 넘기게 됩니다.
안내 문구가 최소 한 번 나와야 하는 안내 후 검증 패턴에는 do while이 어울립니다:
출력:
Enter your age:
"-4" is not a valid age
Enter your age:
"fifteen" is not a valid age
Enter your age:
Age accepted: 15
안내 문구가 루프 안에 있으므로 거부된 답마다 다시 나오고, age는 do 앞에서 선언했으므로 루프 뒤의 코드가 쓸 수 있습니다. 사용자가 키보드로 입력하면 각 답이 안내 문구 뒤 콘솔에 나타납니다. 여기처럼 입력을 리디렉션하면 프로그램 자신의 출력만 보입니다.
break를 쓰는 while (true)
때로는 자연스러운 종료 지점이 본문 중간, 어떤 작업 뒤이자 나머지 작업 앞에 있습니다. 루프를 while (true)로 쓰고 break로 빠져나가세요:
출력:
processing: resize photo
processing: send email
stop signal received
1 job(s) left in the queue
Dequeue는 큐 앞에서 다음 작업을 꺼내고, 루프는 종료 표시를 보자마자 빠져나가므로 마지막 작업은 큐에 남습니다. 종료 지점이 명확하고 본문 위쪽에 가까이 있다면 while (true)는 나쁜 코드의 징후가 아닙니다. 긴 본문 곳곳에 break가 흩어져 있을 때 그렇게 됩니다. 그때는 이름 붙은 bool 조건이나 return을 쓰는 메서드가 따라가기 쉽습니다. 중첩 루프를 빠져나가는 방법은 break와 continue 페이지에서 다룹니다.
끝나지 않는 루프
의도하지 않은 무한 루프의 원인은 세 가지 중 하나입니다.
본문이 조건을 바꾸지 않음. 카운터를 증가시키지 않았거나 엉뚱한 변수를 증가시켰습니다:
int i = 0;
while (i < 5)
{
Console.WriteLine(i); // i++ forgotten: prints 0 forever
}
조건 뒤의 세미콜론. 세미콜론이 루프의 본문 전체가 되고, 그 아래 블록은 (끝나지 않는) 루프 뒤에 실행됩니다:
while (retries < 3); // warning CS0642: Possible mistaken empty statement
{
retries++;
}
목표를 건너뛰는 조건. 변수가 일정 간격으로 움직일 때 !=로 검사하면 멈춰야 할 값을 뛰어넘을 수 있습니다. while (x != 10) x += 3;은 0, 3, 6, 9, 12로 진행하며 10을 그대로 지나칩니다(int가 오버플로해 수십억 단계 뒤에 한 바퀴 돌아야 우연히 10에 닿습니다). 간격이 1이 아니면 != 대신 <나 >로 검사하세요. 정확한 목표에 거의 닿지 않는 double 값도 마찬가지입니다. while (x != 1.0) x += 0.1;은 끝나지 않습니다.
while, do while, for 중에서 고르기
| 상황 | 루프 |
|---|---|
| 반복 횟수를 알거나 인덱스가 필요함 | for |
| 컬렉션의 모든 요소 | foreach |
| 조건이 참인 동안 반복, 0번일 수도 있음 | while |
| 조건이 의미를 갖기 전에 본문이 한 번 실행되어야 함 | do while |
| 종료 검사가 본문 중간에 있음 | break를 쓰는 while (true) |
모든 for 루프는 while 루프로, 그 반대로도 다시 쓸 수 있습니다. 선택은 루프의 로직을 읽는 사람이 기대하는 곳에 두는 문제입니다. 카운터, 검사, 증가가 모두 있다면 for가 이들을 한 줄에 모아 둡니다. 루프가 본문 안에서 바뀌는 상태로 움직인다면 while이 그렇다고 말해 줍니다.
자주 묻는 질문
C#에서 while과 do while의 차이는 무엇인가요?
while 루프는 매 반복 전에 조건을 검사하므로 본문이 한 번도 실행되지 않을 수 있습니다. do { } while (condition); 루프는 매 반복 후에 검사하므로 본문이 항상 최소 한 번 실행됩니다. 입력을 읽고 나서 검증하는 경우처럼, 첫 실행이 조건에서 검사할 값을 만들 때 do while을 쓰세요.
C#에서 do while 루프의 문법은 어떻게 되나요?
do { body } while (condition);입니다. 닫는 괄호 뒤의 세미콜론에 주의하세요. 필수이며, 빠뜨리면 컴파일 오류입니다. 조건에서 본문에서 대입한 변수를 쓸 수 있지만, 본문의 중괄호는 별도의 범위이므로 그 변수는 do 앞에서 선언해야 합니다.
C#에서 while (true)는 어떻게 쓰나요?
while (true) { ... }는 안에서 break, return, 예외로 빠져나갈 때까지 영원히 돕니다. 한 줄을 읽고 비어 있으면 멈추는 경우처럼, 종료 검사가 본문 중간에 있어야 할 때 자연스러운 형태입니다.
C#에서 사용자가 특정 단어를 입력하거나 입력이 끝날 때까지 읽으려면 어떻게 하나요?
입력이 끝나면 null을 반환하는 Console.ReadLine()으로 반복합니다: string line; while ((line = Console.ReadLine()) != null && line != "done") { ... }. 조건 안의 대입이 다음 줄을 읽고 검사하는 것을 한 번에 합니다.
C# while 루프가 끝나지 않는 이유는 무엇인가요?
보통은 본문이 조건에서 검사하는 값을 바꾸지 않거나(i++를 잊음), 조건 바로 뒤에 세미콜론이 있기 때문입니다. while (count < 10);은 본문이 비어 있는 루프로 영원히 돕니다. 컴파일러는 두 번째 경우를 CS0642 Possible mistaken empty statement로 경고합니다.