본문 바로가기

조건 분기

1. 분기

 

분기(branch)는 프로그램의 실행 흐름을 선택하는 것을 말한다.

 

두 수를 입력받아 계산하는 계산기를 만든다고 생각해보자. 

입력받는 것까지는 문제 없다. 하지만 그 다음이 문제다.

사칙연산의 결과를 모두 출력할 수는 있다. 하지만 그 중 하나를 고르는 건 어떻게 해야 할까?

 

이를 위해 필요한 게 분기문이다.

 

분기문은 기본적으로 조건에 의해 이루어진다

다시 말해, 조건에 따라 실행 여부를 결정한다는 것이다.

 

물론 무조건 분기도 있다. 

하지만 상대적으로 사용 빈도가 낮다.

 

앞에서 예시로 든 계산기의 경우 다음과 같이 실행 여부를 결정할 수 있다.

 

수행할 연산을 입력받는다.

입력받은 연산이 +면 덧셈을 수행한다. 아니면 다음 줄로 넘어간다.

입력받은 연산이 -면 뺄셈을 수행한다.

입력받은 연산이 *면 곱셈을 수행한다.

입력받은 연산이 /면 나눗셈을 수행한다.

 

 

 

2. if 문

 

가장 기본적인 분기문으로, 형식은 다음과 같다.

 

if(조건식)

{

//조건식이 참이면 실행

}

 

키워드  if 가 상당히 직관적이기 때문에 이해하는 게 어렵진 않을 것이다.

 

다음은  a 보다  b 가 크면 "b가 a보다 더 큽니다."를 출력하는 코드다.

 

if(a < b

{

printf("a: %d, b:%d\n", a, b);

printf("b

 a보다 

큽니다

.\n");

} 

 

이제 if 문을 사용해 앞에서 예로 든 계산기 프로그램을 작성해보자.

 

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>
 
int main()
{
    int oper;
    double a, b;
    double result;
 
    printf("두 실수: ");
    scanf_s("%lf %lf"&a, &b);
 
    printf("1. 덧셈\n");
    printf("2. 뺄셈\n");
    printf("3. 곱셈\n");
    printf("4. 나눗셈\n");
    printf("선택 >> ");
    scanf_s("%d"&oper);
 
    if (oper == 1
    {
        result = a + b; 
    }
 
    if (oper == 2
    { 
        result = a - b; 
    }
 
    if (oper == 3)
    {
        result = a * b; 
    }
 
    if (oper == 4)
    {
        result = a / b;
    }
 
    printf("결과: %f\n", result);
 
    return 0;
}
cs

 

 

 

 

 

 

 

그러나 이 코드에는 다음의 문제점이 있다.

 

1~4 중 무엇을 입력해도 조건 검사는 항상 모두 실행된다.

 

이 경우 세 번의 무의미한 조건 검사가 일어나고, 이는 성능 저하로 이어진다.

따라서 조건 검사가 다음과 같이 이루어진다면 더 효율적일 것이다.

 

조건을 만족하면 나머지 if 문은 모두 건너뛴다.

 

우리는 if~else 문을 통해 이를 구현할 수 있다.

 

이건 잠시 뒤에 보기로 하고, 우선 다음 문제를 보자.

풀고 싶은 사람은 직접 풀어도 되지만, 아마 아직 어려울 것이다.

 

Q. 10보다 작은 자연수 중에서 3 또는 5의 배수는 3, 5, 6, 9 이고, 이것을 모두 더하면 23입니다.

    1000보다 작은 자연수 중에서 3 또는 5의 배수를 모두 더하면 얼마일까요?

 

출처: http://euler.synap.co.kr/prob_detail.php?id=1

 

더보기

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
 
int main()
{
    int i;
    int sum = 0;
 
    for (i = 1; i < 1000++i)
    {
        if ((i % 3 == 0|| (i % 5 == 0))
        {
            sum += i;
        }
    }
 
    printf("sum: %d\n", sum);
 
    return 0;
}
cs

 

 

 

정답: 233168

 

결과만 제대로 나오면 코드가 어떻든 상관 없다. 손으로 풀지만 않았으면 된다.

 

 

 

 

3. if~else 문

 

형식은 다음과 같다.

 

if( 조건식 )

{

//조건식이 참일 때 실행할 문장 (이 문장을 실행한 다음 else 블록은 건너뛴다.)

}

else

{

//조건식이 거짓일 때 실행할 문장 (if 블록은 건너뛴다.)

}

 

이 때  else 는 반드시  if 와 쌍을 이루어야 한다. 독립적으로 사용할 수 없다는 것이다.

 

다음은 입력한 수가 짝수인지 홀수인지 판별하는 코드다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
int main()
{
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    if (num % 2 == 0)
    {
        printf("짝수\n");
    }
    else
    {
        printf("홀수\n");
    }
 
    return 0;
}
cs

 

 

 

참고로 중괄호는 다음과 같이 생략할 수도 있고,

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
int main()
{
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    if (num % 2 == 0)
        printf("짝수\n");
    else
        printf("홀수\n");
 
    return 0;
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
int main()
{
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    if (num % 2 == 0printf("짝수\n");
    else printf("홀수\n");
 
    return 0;
}
cs

 

다음과 같이 중괄호를 한 줄에 쓸 수도 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
int main()
{
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    if (num % 2 == 0){ printf("짝수\n"); }
    else { printf("홀수\n"); }
 
    return 0;
}
cs

 

 

비단 if~else 문 뿐만 아니라 반복문, 함수 등 모든 문장에서 

내부 코드가 한 줄이면 중괄호는 생략할 수 있고 중괄호를 한 줄에 쓸 수도 있다.

마음대로 쓰면 된다.

 

 

참고로 (일부) if~else 문은 연산자 하나로 대체할 수 있다.

 

다음 코드를 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
int main()
{
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    if (num % 2 == 0)
    {
        printf("짝수\n");
    }
    else
    {
        printf("홀수\n");
    }
 
    return 0;
}
cs

 

이걸 어떻게 연산자 하나로 대체할 수 있을까?

그 답은 바로 삼항 연산자다.

3.2 연산자에서 언급했듯이, 삼항 연산자는 다음과 같이 사용한다.

 

(조건식) ? (참일 때의 반환값) : (거짓일 때의 반환값)

 

그러니까 위 코드는 다음과 같이 바꿀 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int main()
{
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    printf( (num%2 == 0) ? ("짝수\n") : ("홀수\n") );
 
    return 0;
}
cs

 

훨씬 간결하지 않은가?

이처럼 삼항 연산자를 적절히 사용하면 코드를 훨씬 간결하게 만들 수 있다.

 

 

 

 

 

4. if...else if...else 문

 

형식은 다음과 같다.

 

if( 조건식 1 )

{

//조건식 1이 참일 경우 실행

}

else if( 조건식 2 )

{

//조건식 2가 참일 경우 실행

}

else if( 조건식 3 )

{

//조건식 3이 참일 경우 실행

}

 

...

 

else if( 조건식 n )

{

//조건식 n이 참일 경우 실행

}

else

{

조건식 1 ~ 조건식 n이 모두 거짓일 경우 실행

}

 

 

보다시피 else if 절은 얼마든지 추가할 수 있다.

 

여기서 중요한 것은 if 문을 연속으로 쓴 것과의 차이점이다.

 

 

 

if( 조건식 1 ) { 실행문 1 }              if( 조건식 1 ) { 실행문 1 }

if( 조건식 2 ) { 실행문 2 }              else if( 조건식 2 ) { 실행문 2 }

if( 조건식 3 ) { 실행문 3 }      VS      else if( 조건식 3 ) { 실행문 3 }

if( 조건식 4 ) { 실행문 4 }              else if( 조건식 4 ) { 실행문 4 }

else { 실행문 5 }                       else { 실행문 5 }

 

 

 

전자의 경우 무조건 4번의 조건 검사를 한다. 실행문 5조건식 4가 거짓일 때 실행된다.

 

후자의 경우 조건식 1이 참이면 실행문 1을 실행하고 나머지 조건은 검사하지 않는다.

조건식 1이 거짓이면 조건식 2를 검사하고, 

조건식 2가 참이면 실행문 2를 실행하고 나머지 조건은 검사하지 않는다.

 

위 코드에서 if...else if...else 문은 이런 의미다.

 

조건식 1이 참이면 실행문 1만 실행한다.

조건식 1이 거짓이면 조건식 2를 검사한다.

조건식 2가 참이면 실행문 2 실행 후 모두 건너뛰고, 거짓이면 조건식 3을 검사한다.

조건식 3이 참이면 실행문 3 실행 후 모두 건너뛰고, 거짓이면 조건식 4를 검사한다.

조건식 4가 참이면 실행문 4 실행 후 모두 건너뛰고, 거짓이면 실행문 5를 실행한다.

 

다음은 입력받은 정수가 음수인지, 양수인지, 0인지 출력하는 코드다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
 
int main()
{
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    if (num > 0)
    {
        printf("양수\n");
    } 
    else if (num == 0)
    {
        printf("0\n");
    }
    else
    {
        printf("음수\n");
    }
 
    return 0;
}
cs

 

위 코드에서 if...else if...else 문만 떼어 내보자.

 

if (num > 0)

{

printf("양수\n");

else if (num == 0)

{

printf("0\n");

}

else

{

printf("음수\n");

}

 

그리고 이걸 반으로 쪼개보자.

 

if (num > 0)

{

printf("양수\n");

else 

 

if (num == 0)

{

printf("0\n");

}

else

{

printf("음수\n");

}

 

이제 if...else if...else 문의 의미가 보이지 않는가?

가독성을 위해 중괄호를 삽입해보자.

 

if (num > 0)

{

printf("양수\n");

else 

{

if (num == 0)

{

printf("0\n");

}

else

{

printf("음수\n");

}

}

 

이제는 if...else if...else 문의 의미가 보일 것이다.

왜 if 문의 조건식이 참이 되면 나머지를 건너뛰는 지 알 것이다.

 

 

 

 

4. break 문

 

break 문반복문을 탈출할 때 쓰는 문장으로, 탈출하려는 위치에  break; 라고만 써주면 된다.

 

다음은 5가 나올 때까지 주사위를 던지는 프로그램이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
    int dice;
 
    srand((unsigned)time(NULL));
 
    while (1//무한 루프
    {
        printf("주사위를 던집니다: ");
        dice = (rand() % 6+ 1//1~6의 난수 생성
        
        printf("%d\n\n");
 
        if (dice == 5)
            break;
    }
 
    return 0;
}
cs

 

2행과 3행, 9행은 난수를 발생시키기 위한 여러가지 사전작업이고,

rand 함수는 난수(random number)를 발생시키는 함수로 보면 된다.

 

다시 한 번 말하지만, break 문반복문을 탈출하기 위해 사용된다.

if 문을 탈출한다고 착각하지 말자.

또 한 가지 주의할 점은 하나의 반복문만 탈출한다는 것이다.

 

다음은 구구단을 출력하되 2단은 2×2까지, 3단은 3×3까지, , 9단은 9×9까지 출력하는 코드다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
int main()
{
    int i, j;
 
    for (i = 2; i <= 9++i)
    {
        for (j = 1; j <= 9++j)
        {
            printf("%d × %d = %-2d\n", i, j, i*j);
 
            if (i == j) 
                break;
        }
 
        printf("\n");
    }
 
    return 0;
}
cs

 

 

 

코드를 보면 알 수 있듯이, break 문은 안쪽 반복문만 탈출한다.

만약 바깥쪽 반복문까지 탈출했다면 2단만 출력되었을 것이다.

 

결론적으로, break 문의 기능은 다음과 같이 정의할 수 있다.

 

break 문은 가장 가까운 반복문 하나만을 탈출한다.

 

 

 

 

5. continue 문

 

continue 문은 반복문에서 한 차례를 건너뛸 때 사용하는 문장이다.

break 문과 마찬가지로 건너뛸 곳에서  continue; 라고만 써주면 된다.

 

다음은 입력받은 수에 해당하는 구구단을 출력하되, 홀수단만 출력하는 프로그램이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
int main()
{
    int i;
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    for (i = 2; i <= 9++i)
    {
        if (i % 2 == 0
            continue;
 
        printf("%d × %d = %d\n", num, i, num*i);
    }
 
    return 0;
}
cs

 

 

continue 문 역시 break 문과 마찬가지로 하나의 반복문에만 적용된다.

 

다음은 구구단을 출력하되 2×2, 3×3, , 9×9를 제외하고 출력하는 코드다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
int main()
{
    int i, j;
 
    for (i = 2; i <= 9++i)
    {
        for (j = 1; j <= 9++j)
        {        
            if (i == j)
                continue;
 
            printf("%d × %d = %-2d\n", i, j, i*j);
        }
 
        printf("\n");
    }
 
    return 0;
}
cs

 

 

 

 

 

6. break 문  VS  continue 문

 

break 문continue 문은 헷갈리기 쉽다.

사실 필자도 처음 공부할 당시에 많이 헷갈렸다.

때문에 한 번 정리할 필요가 있다.

 

우선 문장을 만났을 때 어딘가로 이동한다(실행 흐름이 바뀐다)는 것은 같다.

하지만 그 목적지가 다르다. 사실 이것 말고는 차이가 없다.

 

코드로 보면 다음과 같다.

 

 

 

 

 

 

 

 

반복문을 탈출한다.

 

 

조건 검사 부분으로 이동한다.

 

 

 

 

 

7. switch 문

 

switch 문은 다음과 같은 형식으로 사용한다.

 

switch(n)

{

case v1: 

//n의 값이 v1일 때 실행

break;

case v2: 

//n의 값이 v2일 때 실행

break;

...

case vn: 

//n의 값이 vn일 때 실행

break;

default:

//n의 값이 v1~vn 중 아무것도 아닐 때 실행

break;

}

 

 v1: 과 같이 콜론으로 표시된 영역은 *레이블이라 한다.

각 레이블의 끝에 위치한 break 문은 실행 영역을 묶기 위함이다.

이에 대해서는 조금 뒤에 이야기하겠다.

 

참고로  n 에는 정수형만 올 수 있다.

그러니 정수 연산의 결과나 문자는 되지만 문자열과 실수는 쓸 수 없다.

 

 

*레이블(label): 실행의 대상이 아닌 위치를 표시하는 데 쓰이는 표식

 

 

다음은 주사위를 던져 나온 수를 영어로 출력하는 프로그램이다. 

 

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
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
    int dice;
 
    srand((unsigned)time(NULL));
    dice = (rand() % 6)+ 1;
 
    switch (dice)
    {
    case 1:
        printf("one\n");
        break;
    case 2:
        printf("two\n");
        break;
    case 3:
        printf("three\n");
        break;
    case 4:
        printf("four\n");
        break;
    case 5:
        printf("five\n");
        break;
    default:
        printf("six\n");
        break;
    }
 
    return 0;
} 
cs

 

이번에도 역시 레이블마다 break 문이 있다.

번거롭게 왜 쓰는 걸까?

이유는 다음 코드를 실행해보면 바로 알 수 있다.

 

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
26
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
    int dice = 1;
 
    switch (dice)
    {
    case 1:
        printf("one\n");
    case 2:
        printf("two\n");
    case 3:
        printf("three\n");
    case 4:
        printf("four\n");
    case 5:
        printf("five\n");
    default:
        printf("six\n");
    }
 
    return 0;
}
cs

 

앞에서와 달리 dice 값을 1로 초기화했다.

따라서 예상되는 결과는  "one" 만 출력되는 것이다.

 

 

예상과 달리 one 부터 six까지 모두 출력되었다.

왜 이런 걸까?

 

사실  case 1: 은  dice 가  1 일 때 그 부분만 실행하라는 의미가 아니다.

그 부분부터 쭉, 그러니까  default: 까지 실행하라는 의미다.

앞에서 break 문을 넣은 것도 이 때문이다. 

반복문에서의 break 문도 결국 반복문 내부 코드 실행을 중단하라는 의미를 담고 있지 않은가?

그러니 switch 문에서는 switch 문 내부 코드 실행을 중단하고 탈출하라는 의미인 것이다.

이런 이유로  default: 에는 break 문이 없어도 된다(사실 불필요하다).

 

그렇다고 무조건 break 문을 넣을 필요는 없다.

다음은 주사위를 1, 3, 6일 때는 당첨이고 dice가 2, 4, 5일 때는 낙첨인 복권을 구현한 코드다.

 

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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
    char dice;
    
    srand((unsigned)time(NULL));
    dice = (rand() % 6+ 1;
    
    switch (dice)
    {
    case 1
    case 3:
    case 6:
        printf("당첨!\n");
        break;
 
    case 2: case 4: case 5:
        printf("낙첨!\n");
    }
 
    return 0;
}
cs

 

 case 1: 과  case 3: 에 break 문이 없는 것을 주목하라.

앞에서 말했듯이 1이든 이든 break 문이 없으면 쭉 실행되기 때문에, 결국 세 레이블을

한 곳에 묶은 효과를 낸다.

이를 강조하기 위해 20행와 같이 한 줄에 레이블을 나타내는 것이 일반적이다.

 

사실 switch 문은 if...else if...else 문으로 바꿔쓸 수 있다.

 

다음을 보자.

 

 

 

 

그렇다. 사실 switch 문을 써야만 하는 경우는 없다. 그렇다면 왜 switch 문을 쓰는 걸까?

개인적으로는 가독성 때문이라고 생각한다.

필자의 눈에는 왼쪽의 코드가 더 눈에 잘 들어온다.

그리고 필자 뿐만 아니라 대다수의 프로그래머들이 switch 문을 선호한다.

프로그래밍은 혼자 하는 일이 아니므로, 때로는 대세에 따를 필요가 있다.

 

한 가지 기준을 제시하자면, 분기의 수가 많을 때는 switch 문을 쓰는 게 더 낫다.

하지만 분기 수가 많아도 if..else if...else 문이 더 적절한 경우도 있다.

 

다음을 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
    int score;
 
    if ((90 < score) && (score <= 100))
        printf("A\n");
 
    else if ((80 < score) && (score <= 90))
        printf("B\n");
 
    else if ((70 < score) && (score <= 80))
        printf("C\n");
 
    else
        printf("F\n");
 
    return 0;
}
cs

 

switch 문으로 옮겨 쓸 수 있을까?

물론 가능하긴 하다. 다만 번거롭다.

71, 72, , 100까지 하나하나 레이블을 작성한다고 생각해보면....

 

그러니 분기 수가 많다고 switch 문을 고집할 필요는 없다.

기본적으로 switch 문은 정수값에 따라 분기하고

if..else if...else 문은 참/거짓에 따라 분기하기 때문에 그에 맞게 쓰면 된다.

 

 

 

8. goto 문

 

사실 이 내용을 쓸지 말지 고민했지만, 그래도 아는 게 낫다고 생각해 쓰기로 했다.

goto 문은 이름에서 알 수 있듯이 특정 위치, 즉 특정 레이블로 이동하는 문장이다.

그러나 현재는 사용을 지양하고 있다.

이유는 단순하다. 프로그램의 실행 흐름을 방해하기 때문이다.

어차피 쓰질 않으니 몰라도 되긴 하지만, 모르고 안 쓰는 것과 알고 안 쓰는 것은 다르다.

지식은 곧 힘이라는 말도 있지 않은가? 그러니 하나라도 더 알아두자.

 

형식은 다음과 같다.

 

LABEL:

...

goto LABEL;

...

 

다음 코드를 보자.

 

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
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
    int num;
 
    printf("num: ");
    scanf_s("%d"&num);
 
    if (num == 1) { goto ONE; }
    else if (num == 2) { goto TWO; }
    else { goto OTHER; }
 
ONE:
    printf("one\n");
    goto END;
TWO:
    printf("two\n");
    goto END;
OTHER:
    printf("I don't know\n");
    goto END;
 
END:
    printf("프로그램을 종료합니다.\n");
 
    return 0;
}
cs

 

 

 

 num  값이  2 이므로  TWO  레이블로 이동하고  TWO  레이블에서는  END  레이블로 이동한다.

 

 

 

'Programming > C' 카테고리의 다른 글

변수  (0) 2019.03.07
함수  (0) 2019.03.06
반복문  (0) 2019.03.03
입력과 출력  (0) 2019.03.02
연산자  (0) 2019.03.01