7장 반복문

반복문은 일정 구간의 코드를 연속적으로 반복해 실행하는 제어문 이다.

반복문에서 중요한것은 반복횟수이고 더 중요한것은 반복을 멈추기 위한 조건식이다.

7.1 while - 조건 기반 반복문

while문은 내부 구문이 끝나면 그다음으로 그냥 넘어가는 것이 아니라 다시 구문의 처음으로 돌아가 조건식을 확인한다. 그리고 조건이 참이면 계속해서 구문을 실행한다.

7.1.1 기본구조 조건에 의한 제어

예제 코드는 사용자로부터 문자 혹은 문자열을 입력받은 후, 출력하는 코드를 계속 반복하는것이다. 단, 사용자가 입력한 글자가 개행문자가 아닌 동안만 그렇게 하기 때문에 사용자가 개행문자를 입력한 경우 버퍼에서 한 글자씩 읽어와 putchar()함수로 출력한다.

#include <stdio.h>

int main(void){
    char ch = 0;
    while((ch = getchar()) != '\n'){
        putchar(ch);
    }
    
    return 1;
}
실행결과
❯ ./welcome
HelloWorld
HelloWorld%  

07-01 사용자가 입력한 숫자만큼 * 출력하기
사용자로부터 1~9범위의 정수를 입력받아 그 수만큼 '*'을 출력하는 프로그램을 작성한다.
'*'은 모두 한 행에 연이어 출력하며, 최조 사용자 입력이 범위를 벗어났다면 1이나 9로 강제 보정한다.
정해진 개수만큼 '*'을 출력한 후에는 반드시 개행 한다.
#include <stdio.h>

int main(void){
    int nInput;
    printf("숫자를 입력하세요 : ");
    scanf("%d", &nInput);
    
    if(nInput < 1){
        nInput =  1;
    }
    else if(nInput > 9){
        nInput = 9;
    }

    while(nInput--){
        putchar('*');
    }
    putchar('\n');
    return 1;
    
}

실행결과
❯ ./welcome
숫자를 입력하세요 : -10
*
❯ ./welcome
숫자를 입력하세요 : 19
*********

7.1.2 무한루프

반복문에서 가장 신경 써야 할 사항은 반복을 멈추기 위한 조건이다. 그리고 그 조건에 영향을 줄수 있는 연산이 반복문 내부에 포함되어야한다.

다음 예제는 반복문 내부에 반복을 멈추는 조건에 도달하지 못하여 무한 루프를 도는 코드이다.

#include <stdio.h>

int main(void){
    int nIndex = 0;
    while(nIndex >= 0){
        ++nIndex;
    }
    printf("%d\n", nIndex);
    return 1;
}

실행결과
❯ ./welcome
-2147483648

핵심은 반복문 내부 연산으로 반복문을 끝내는 상태에 도달하게 프로그램을 작성해야한다.

그런데 특이하게도 프로그램이 끝난다. 이유는 int형 정수의 양수 표현범위를 넘기는 비정상적인 방법으로 프로그램이 끝난다. 32비트 정수 표현을 넘겨서 음수가 된다.

7.1.3 반복문 내부에 선언한 자동변수

반복문 내부에 변수를 선언하는 것은 바람직하지 않다.

이유는 비효율적이기 때문이다.

다음 예제 코드를 보면 nIndex 의 값이 0으로 출력되었다. 스코프가 닫히면 그 내부에 선언된 정의한 변수는 사라진다.

#include <stdio.h>

int main(void){
    char ch = 0;
    while((ch = getchar()) != '\n' ){
        int nIndex = 0;
        printf("%d\t%c\n", nIndex, ch);
        nIndex++;
    }
    return 1;
}

실행결과
❯ ./welcome
Hello
0       H
0       e
0       l
0       l
0       o

7.1.4 반복문의 중첩

while문 내부에 또 다른 while문을 중첩 하는것이다.
while(조건식){
    while(조건식){
        ...
    }
}

구구단 출력 예제

#include <stdio.h>

int main(void){
    int i = 2;
    int j = 1;
    while(i <=9){
        j=1;
        while(j<=9){
            printf("%d * %d = %d\n", i,j,i*j);
            ++j;
        }
        putchar('\n');
        ++i;
    }
    return 1;
    
}

실행결과
❯ ./welcome
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8
2 * 5 = 10
2 * 6 = 12
2 * 7 = 14
2 * 8 = 16
2 * 9 = 18
...
9 * 1 = 9
9 * 2 = 18
9 * 3 = 27
9 * 4 = 36
9 * 5 = 45
9 * 6 = 54
9 * 7 = 63
9 * 8 = 72
9 * 9 = 81
07-02 5행 5열 사각형 출력하기
출력 예와 같이 5행 5열로 '*' 을 출력하는 프로그램을 작성.

#include <stdio.h>

int main(void){
    int i = 0;
    int j = 0;

    while(i < 5){
        j = 0;
        while(j < 5){
            printf("*\t");
            ++j;
        }
        putchar('\n');
        ++i;
    }
    return 1;
    
}

실행결과
❯ ./welcome
*       *       *       *       *
*       *       *       *       *
*       *       *       *       *
*       *       *       *       *
*       *       *       *       *

7.2 for, 계수 기반 반복문

for 문은 while 과 같이 반복문이라는 점에서 같지만, while 문은 조건에 기반을 둔 측면이 강하고 for문은 계수에 기반을 둔 측면이 강하다. while문을 사용할때는 계수기 초기화 코드를 빼먹거나 반복문 내부에 반드시 기술되어야 하는 증감식을 빼먹어버리는 실수를 할 가능성이 있다.

그러나 for문은 그 세가지 요소를 모두 한 행에 기술하도록 하는 문법이다.

반복횟수에 영향을 주는 계수기 초기화, 조건식, 증감 등의 코드를 모두 한행에서 확인할수 있기 때문에 몇 회 반복하는지 쉽게 파악하고 인지할수있다.

#include <stdio.h>
int main(void){
    int i = 0;
    //계수기 초기화;조건식;계수기 증감;
    for(i= 0; i < 5; i++){
        printf("%d\n", i);
    }
    return 1;
}

실행결과
❯ ./welcome
0
1
2
3
4

07-03 1에서 10까지의 총합 구하기
1~10까지의 총합을 출력하는 프로그램을 작성한다. while문을 이용하여 1~10까지의 합을 모두 누적하는 방식으로 구현한다.

#include <stdio.h>

int main(void){
    int i = 1;
    int sum = 0;
    while(i <= 10){
        sum += i;
        i++;
    }
    printf("Total : %d\n", sum);
    return 1;
}

실행결과
❯ ./welcome
Total : 55

07-04 구구단에서 한 단만 출력하기
사용자로부터 2~9사이의 정수를 입력받아 해당 정수의 구구단을 출력하는 프로그램을 작성. 반드시 while문을 이용하여 작성.

#include <stdio.h>

int main(void){
    int nInput;
    int j = 1;
    printf("단을 입력하세요 : ");
    scanf("%d", &nInput);

    if(nInput >= 2 && nInput <= 9){
        j = 1;
        while(j<=9){
            printf("%d * %d = %d\n", nInput , j, nInput*j);
            j++;
        }
    }
    else{
        puts("ERROR");
    }

    return 1;
}

실행결과
❯ ./welcome
단을 입력하세요 : 8
8 * 1 = 8
8 * 2 = 16
8 * 3 = 24
8 * 4 = 32
8 * 5 = 40
8 * 6 = 48
8 * 7 = 56
8 * 8 = 64
8 * 9 = 72
❯ ./welcome
단을 입력하세요 : -10
ERROR

7.2.1 while문과 비교

5행 5열로 '*'을 출력하는 예제를 for문으로 구현한것이다. while 문으로 구현하는것보다 상대적으로 간결해 보인다는것을 알수 있다.

#include <stdio.h>

int main(void){
    int i = 0, j =0;

    for(i=0;i<5;i++){
        for(j=0;j<5;j++){
            printf("*\t");
        }
        putchar('\n');
    }
    return 1;
    
}

실행결과
❯ ./welcome
*       *       *       *       *
*       *       *       *       *
*       *       *       *       *
*       *       *       *       *
*       *       *       *       *

이 예제의 논리구조는 변수 i와 j는 행과 열의 인덱스임을 알수있다.

'*'를 왼쪽에서 오른쪽으로 이어 붙여 출력하는 동안 열의 인덱스 j는 증가하고 행의 인덱스인 i는 위에서 아래쪽으로 개행하면서 증가한다.

7.2.2 '*'를 이용한 도형출력 실습

07-05 첫 번째 직각 삼각형 출력하기
'출력 예'와 같이 '*'를 출력하는 프로그램을 작성한다. 힌트는 행이 증가함에 따라 출력할 '*'의 개수가 1씩 증가한다는 것이다.
*
* *
* * *
* * * *
* * * * *

#include <stdio.h>

int main(void){
    int i = 0, j =0;

    for(i=0; i<5; i++){

        for(j=0; j<i+1; j++){
            printf("*\t");
        }
        putchar('\n');
    }
    return 1;
    
}

실행결과
❯ ./welcome
*
*       *
*       *       *
*       *       *       *
*       *       *       *       *
07-06 두 번째 직각 삼각형 출력하기
'출력 예'와 같이 '*'를 출력하는 프로그램을 작성한다. 힌트는 행이 증가함에 따라 출력할 '*'의 개수가 1씩 증가한다는 것이다. 그리고 '*'가 출력되지 않은 왼쪽 여백은 아무 것도 출력하지 않은 것이 아니라 \t 을 출력한것이다.

#include <stdio.h>

int main(void){
    int i = 0, j =0;

    for(i=0; i<5; i++){

        for(j=0; j<5; j++){
            if(i+j >=4){
                printf("*\t");
            }
            else{
                printf("\t");
            }
        }
        putchar('\n');
    }
    return 1;
    
}

실행결과
❯ ./welcome
                                *
                        *       *
                *       *       *
        *       *       *       *
*       *       *       *       *
07-07 피라미드 출력하기
'출력 예'와 같이 '*'를 출력하는 프로그램을 작성한다.
위 예제들과 가장 큰 차이는 '*'의 개수가 행의 증가에 대해 1씩 증가하지 않는다는 점이다.
#include <stdio.h>

int main(void){
    int i = 0;
    int j = 0;

    for(i = 0; i < 5; i++){

        for(j=0; j<5+i; j++){
            if(i + j >= 4){
                printf("*\t");
            }
            else{
                putchar('\t');
            }
        }

        putchar('\n');
    }
    return 1;
}

실행결과
❯ ./welcome
                                *
                        *       *       *
                *       *       *       *       *
        *       *       *       *       *       *       *
*       *       *       *       *       *       *       *       *

7.3 do while문

do while문은 반복대상 단위코드를 조건과 상관없이 일단 한 번은 실행한 후, 조건을 비교한다.

조건식 괄호 뒤에 세미콜론이 붙는다는 특징도 가지고있다.

#include <stdio.h>

int main(void){
    char ch = 0;

    do{
        ch = getchar();
        putchar(ch);
    }
    while(ch != '\n');
    
    return 1;
}

실행결과
❯ ./welcome
TestString 
TestString

이 예제는 사용자로부터 문자 혹은 문자열을 입력받아 그대로 화면에 출력하는 예제이다.

do while문은 사용자가 입력한 값이 유효범위에 속하는 값인지 프로그램 내부에서 확인하는 로직에 사용할수있다.

7.4 break와 continue

break문은 프로그램의 흐름 내부를 벗어나게 하는 제어문이다.
continue문은 반복문 내부에만 사용할수있으며 수행 즉시 나머지 구문들을 생략하고 다시 조건식을 비교하는 제어문 이다.

이런 제어문을 사용하는 이유는 어떤 연산에서 예외 혹은 어떤 문제가 발생했을 때 이에 대응하기 위함이다.

#include <stdio.h>

int main(void){
    int i = 0;

    for(i = 0; i< 10; ++i){
        if(i > 4) break;
        printf("%d번째\n",i);
    }

    printf("END: i == %d\n",i);
    return 1;
}

실행결과
❯ ./welcome
0번째
1번째
2번째
3번째
4번째
END: i == 5

일반적으로 break문과 continue문은 단독으로 사용되는 경우는 없고 대부분 if문과 결합하여 특정 조건일 때 작동하도록 작성한다.

i가 4보다 크다는 조건을 만족하면 break문을 실행하고 남은 코드를 실행하지 않고 즉시 반복문을 탈출한다.

#include <stdio.h>

int main(void){
    int i = 0;

    for(i = 0; i< 10; ++i){
        if(i > 4) continue;
        printf("%d번째\n",i);
    }

    printf("END: i == %d\n",i);
    return 1;
}

실행결과
❯ ./welcome
0번째
1번째
2번째
3번째
4번째
END: i == 10

break문을 continue로 바뀌었는데 반복문을 벗어나지는 않는다는 점에서 다르다.

break문은 반복을 끝내자는 것이고 continue문은 일부 코드의 생략을 감수하고서도 계속 반복하자는 것이다.

중첩된 반복문에서 break문과 continue문을 실행하면 안쪽 반복문 내부에서 break문을 실행하면 안쪽 반복문만 탈출하고 바깥쪽 반복문은 여전히 유효한 상태가 된다.

ㅇㄴ