(C#) 8. 코드의 흐름 제어 (for)
while로 쓰던 반복을 for로 옮기면서 확인한 것. 초기화·조건·증감을 한 줄에 모으는 것 말고도 반복 변수의 수명이 달라진다는 점, 실수로 무한 루프가 되는 형태를 정리했다.
while 을 한 줄로 모으기
7편에서 “조건에 쓰인 변수가 루프 안에서 바뀌는지 확인해야 한다”고 적었다. 횟수를 세는 반복은 늘 세 가지가 세트로 붙어 다닌다. 시작값을 정하고, 조건을 보고, 값을 하나 올린다.
이 셋이 코드에 흩어져 있으면 하나를 빠뜨리기 쉽다. for는 그걸 한 줄에 모은다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
namespace forEX
{
class Program
{
static void Main(string[] args)
{
int count = 0;
//while (count < 5)
//{
// Console.WriteLine("Hello World");
// count++;
//}
// for (초기화식; 조건식; 반복식)
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Hello World");
}
}
}
}
주석으로 남겨둔 while 버전과 for 버전이 하는 일은 완전히 같다. 실행 순서도 같다.
- 초기화식
int i = 0(한 번만) - 조건식
i < 5검사 - 참이면 본문 실행
- 반복식
i++ - 다시 2번으로
반복 변수가 밖에 남지 않는다
두 버전의 진짜 차이는 여기다.
1
2
3
4
5
6
int count = 0;
while (count < 5) { count++; }
// 여기서 count 는 5 로 남아 있다
for (int i = 0; i < 5; i++) { }
// 여기서 i 는 존재하지 않는다. 쓰면 컴파일 에러
for 안에서 선언한 i는 그 루프 안에서만 산다. 이게 실용적으로 꽤 중요했다.
첫째, 이름을 재사용할 수 있다. 루프를 여러 개 쓸 때 전부 i로 써도 충돌하지 않는다. while이었다면 count, count2처럼 이름을 늘려가야 한다.
둘째, 루프가 끝난 뒤 실수로 그 변수를 쓰는 일이 없다. 루프가 끝난 시점의 count가 5인지 4인지 헷갈릴 여지 자체가 사라진다.
반복이 끝난 뒤에도 값이 필요하면 밖에 선언하면 된다. for의 초기화식은 비워둘 수 있다.
1
2
3
int i;
for (i = 0; i < 5; i++) { }
Console.WriteLine(i); // 5
자주 하는 실수 몇 가지
세미콜론을 붙여버리기
1
2
3
4
for (int i = 0; i < 5; i++); // 여기 세미콜론
{
Console.WriteLine("Hello"); // 한 번만 실행된다
}
세미콜론 하나가 빈 본문이 되어, 루프는 아무것도 안 하고 5번 돌고 끝난다. 아래 중괄호는 그냥 블록 하나라 한 번만 실행된다. 눈으로는 거의 안 보인다.
부등호 방향과 경계
1
2
for (int i = 0; i <= 5; i++) // 6번 돈다
for (int i = 1; i < 5; i++) // 4번 돈다
0부터 < n이 n번이라는 걸 기본으로 삼으면 헷갈릴 일이 준다. 배열 인덱스가 0부터 시작하니 for (int i = 0; i < arr.Length; i++)가 그대로 전체 순회가 된다.
조건에서 값을 안 바꾸는 형태
1
2
3
4
for (int i = 0; i < 5; ) // 반복식이 비어 있다
{
Console.WriteLine("Hello"); // 무한 루프
}
for도 세 칸 중 아무거나 비울 수 있다. 반복식을 비우고 본문에서 올려주는 경우가 있는데, 그걸 잊으면 while과 똑같이 무한 루프가 된다.
일부러 무한 루프를 만들려면 셋 다 비운다.
1
for (;;) { } // while (true) 와 같다
루프 안에서 반복 변수를 건드리기
1
2
3
4
for (int i = 0; i < 10; i++)
{
if (조건) i--; // 조건이 계속 참이면 끝나지 않는다
}
문법적으로는 되지만 루프 횟수를 예측할 수 없어진다. 이런 게 필요하면 while로 쓰는 편이 의도가 분명하다. for는 “정해진 횟수만큼 돈다”는 신호로 읽히기 때문이다.
역순과 여러 변수
for는 증가만 하는 게 아니다.
1
2
for (int i = 4; i >= 0; i--) // 4, 3, 2, 1, 0
for (int i = 0; i < 100; i += 10) // 0, 10, 20, ...
역순 루프는 리스트에서 원소를 지우면서 순회할 때 꼭 필요해진다. 앞에서부터 지우면 뒤 원소들이 앞으로 당겨져 인덱스가 어긋나는데, 뒤에서부터 지우면 아직 안 본 앞쪽 인덱스가 안 흔들린다.
변수 두 개를 같이 움직일 수도 있다. 쉼표로 나열한다.
1
2
3
4
for (int i = 0, j = 10; i < j; i++, j--)
{
// 양쪽에서 가운데로
}
문자열이나 배열을 뒤집을 때 쓰는 형태다. 다만 여기서 int i = 0, j = 10은 되지만 int i = 0, string s = ""처럼 타입이 다른 건 안 된다.
for 와 while 을 언제 고르나
정리하면서 기준이 이렇게 잡혔다.
- 횟수가 정해져 있다 →
for. 시작, 끝, 증가가 한 줄에 보인다 - 조건이 만족될 때까지 →
while. 몇 번 돌지 모를 때 - 최소 한 번은 해야 한다 →
do-while
컬렉션 전체를 순회하는 경우는 for보다 foreach가 더 낫다. 인덱스를 직접 다루지 않으니 경계 실수가 아예 없어진다. 그건 26편에서 배열을 다룰 때 나온다.
정리하면
for는 초기화·조건·반복식을 한 줄에 모은 것이다.while로 쓸 수 있는 걸 짧게 쓴 것뿐이다for안에서 선언한 변수는 루프 밖에 남지 않는다. 이름을 재사용할 수 있고 실수로 쓸 일도 없다for (...);처럼 세미콜론이 붙으면 본문이 비어버린다- 세 칸 중 아무거나 비울 수 있고, 반복식을 비우면 무한 루프가 된다
- 리스트에서 지우면서 순회할 때는 역순으로 돈다
- 횟수가 정해졌으면
for, 조건으로 끝나면while