일
KNK Chap.6 (6.3) 본문
6.3 for문
사실상 for문이 루프의 상당 부분을 차지함.
특히 숫자를 세야 하는 변수가 있을 때 아주 유용한 반복문임.
for문은 아래와 같은 구조를 가짐.
for ( expr1 ; expr2 ; expr3 ) statement
예제를 들어 설명하자면
for (i = 10; i > 0; --i) {
printf("T 빼기 %d, 세는 중\n", i);
}
예제의 for문이 실행되면 변수 i는 10으로 초기화되고, i > 0인지 판별함.
10 > 0 이므로 T 빼기 %d, 세는 중 메시지가 출력되고, i는 감소함.
그 다음 다시 i > 0 을 판별하고, i는 10부터 1까지 지속되므로 루프 본문은 총 10번 실행됨.
몇가지 특수한 경우를 제외하고 while문은 for문으로 대체할 수 있음.
예를 들어
expr1;
while ( expr2 ) {
statements
expr3;
}
이런 식으로 for문을 while문 형식으로 교환할 수 있음.
for문의 관용적 표현
for문은 일정 횟수 또는 구간에서 변수를 증감시키는 동안 반복을 할 때 유용함.
주로 n번 숫자를 세야 할 때 for문이 사용됨. 아래는 관용적 표현들임.
○ 0에서 n-1까지
for (i = 0; i < n; ++i) ...
○ 1에서 n까지
for (i = 1; i <= n; ++i); ...
○ n-1에서 0까지
for (i = n - 1; i >= 0; --i) ...
○ n에서 1까지
for (i = n; i > 0; --i) ...
이런 관용적 표현들을 외워서 사용하면 다음과 같은 흔한 에러를 방지할 수 있음.
○ > 대신 < 를 사용하거나 그 반대의 경우.
변수가 감소하는 경우엔 > 혹은 >= 사용하고, 증가하는 경우엔 < 혹은 <= 연산자를 사용함.
○ 제어식에서 < , <= , > , >= 연산자 대신 == 연산자를 사용하는 경우.
○ 제어식을 i < n 대신 i <= n으로 쓸 때 발생하는 "하나 부족한" (Off-by-one Error.OBOE) 에러의 경우.
for문에서 표현식 생략하기
C언어는 for문의 표현식을 생략할 수 있게 함.
단 첫번째 표현식이 생략되면 루프 실행 전 아무런 초기화도 일어나지 않음. 예제↓
i = 10;
for (; i > 0; --i) {
printf("T 빼기 %d, 세는 중\n", i);
}
for문 이전에 이미 i가 따로 10이라는 값을 할당받고 초기화가 되어 있어 첫번째 표현식 생략함.
여기서 첫번째 표현식의 세미콜론;은 생략되지 않았음. 이건 생략하면 안됨.
만약 세번째 표현식을 생략하려면 루프 본문에서 두번째 표현식이 언젠가는 거짓이 되게 해야 함.
예를 들어 위 예제를 아래와 같이 바꿀 수 있음.
for (i = 10; i > 0;) {
printf("T 빼기 %d, 세는 중\n", --i);
}
세번째 표현식을 생략했기 때문에 그 역할을 루프본문 내에서 --i를 통해 i를 감소시킴.
만약 첫번째 표현식과 세번째 표현식이 둘다 생략되면 사실상 for문을 뒤집어 쓴 while문임.
예제 ↓
for (; i > 0;) {
printf("T 빼기 %d, 세는 중\n", --i);
}
위 루프는 사실 아래와 같은 루프임.
while (i > 0) {
printf("T 빼기 %d, 세는 중\n", --i);
}
이럴 경우는 while문으로 작성하는게 더 나음.
만약 두번째 표현식을 생략한다면, for문은 이를 참값으로 단정하여 종료되지 않음.
그래서 그런 경우는 아래처럼 일부러 무한루프를 만들고자 할 때만 사용하는게 좋음.
[관용구]
for (;;) ...
C99에서의 for문
[C99] for문의 첫번째 표현식은 선언문으로 대체할 수 있음.
즉, 루프에서 사용할 변수를 선언하는게 가능함.
for (int i = 0; i < n; ++i) {
...
}
여기서 i라는 변수는 for문 이전에 선언해줄 필요가 없음.
만약 그 이전에 선언된 적이 있다면, 이 for문은 아예 루프 내에서만 사용되는 새로운 i를 만듦.
for문 내에서 선언된 변수는 루프본문 밖에서 사용할 수 없음.
보통 루프 밖에서 찾을 수 없다고 함. 예제 ↓
for (int t = 0; i < n; ++i) {
...
printf("%d", i); /* 가능; i는 루프 내에서 찾을 수 있다 */
...
}
printf("%d", i); /*** 틀림 ***/
for문 내에서 따로 사용할 변수를 선언해주는 것은 상당히 유용함.
간편하고, 가독성을 높여줌.
하지만 루프가 종료된 후에도 해당 변수를 사용해야 한다면 C89 기준 방식으로 사용해야 함.
for문은 여러개의 동일 형식 변수를 선언할 수 있음.
for (int i = 0; j = 0; i < n; ++i) {
…
}
쉼표연산자
종종 for문 내에 두 개 (이상)의 표현식을 초기화하거나
루프가 실행될 때마다 여러 변수를 증감시켜야 할 때가 있음.
for문의 첫번째와 세번째 표현식 내에서 쉼표연산자를 통해 구현 가능함.
쉼표식의 구조 ↓
expr1, expr2
expr1, expr2는 임의의 표현식임.
쉼표식은 우선 expr1이 평가 되고 그 값은 버려짐. 그 후 expr2가 평가되고 그 값이 전체 표현식 값임.
expr1을 평가할 때 언제나 부작용이 발생하는데, 부작용이 없으면 무의미한 표현식임.
예를 들자면, 값이 1인 변수 i, 값이 5인 변수 j가 있다 치자.
쉼표식 i, i + j가 평가되면 우선 i가 2로 증가되고 나서 i + j 가 평가될 것임.
그래서 전체 표현식의 값은 7이 됨.
쉼표연산자의 평가순서는 다른 연산자보다 낮아서 i와 i + j를 괄호치는 의미는 없음.
또한 좌향결합적이기 때문에 쉼표식을 이어 쓸 수 있음. 예를 들어
i = 1, j = 2, k = i + j
는 아래 코드와 같음
((i = 1), (j = 2)),(k = (I + j))
쉼표식에선 좌측 피연산자가 우측 피연산자보다 먼저 평가됨.
따라서 할당식 i = 1, j = 2, k = i + j 는 왼쪽에서 오른쪽 순서로 진행됨.
쉼표연산자는 C언어가 형식상 한개의 표현식만을 요구할 때, 두 개 이상의 표현식을 쓸 수 있게 함.
복합문과 기능이 비슷하다고 볼 수도 있지만, 사실 위의 상황은 자주 생기지는 않음.
예를 들어 for문에서 두 개의 변수를 초기화하고 싶을 때 쉼표 연산자를 사용하면
for (sum = 0, i = 1; i <= N; ++i) {
sum += i;
}
이렇게 sum = 0, i = 1 을 쉼표연산자로 이어서 쓰면서
sum에 0을 할당하고 i에 1을 할당할 수 있음.
[프로그래밍] 제곱표 출력하기 (수정)
6.1에서 만든 square.c 프로그램은 for문을 이용하여 코드를 개선할 수 있음.
위 코드를 보면, C언어는 for문의 행동을 제어하는 세 개의 표현식에 어떤 제한도 두지 않음.
i 말고 다른 변수를 추가할 수도 있음.
위 for문에선 square라는 변수를 초기화하고, i를 평가한 후, odd의 값을 증가시킴.
i는 제곱할 숫자, odd는 현재 제곱수에 더하여 다음 제곱수를 구할 때 쓰이는 변수임.
이를 통해 곱하기 연산을 사용하지 않고 제곱수를 구할 수 있음.
그러나, 오용할 수 있는 가능성이 있기 때문에 i 변수 하나로 for문을 제어하는 square2.c가 나음.
'C' 카테고리의 다른 글
KNK Chap.6 (6.5~Q&A) (0) | 2022.12.19 |
---|---|
KNK Chap.6 (6.4) (0) | 2022.12.16 |
KNK Chap.6 (6.2) (0) | 2022.12.16 |
KNK Chap.6 (6.1) (0) | 2022.12.16 |
KNK Chap.5 (Q&A) (0) | 2022.12.16 |