C

C기초 플러스 chap.7 C의 제어문: 분기와 점프

Barbarian developer 2024. 8. 11. 13:25

여기까지 따라 왔다면 좀만 더 가면 된다!! 따라와라!! 우린 할수 있따!!!

베헤에에에에엘라!!!


제어문(control flow statements)

전장인 6장에서 우린 이미 제어문을 공부했다!!

 

C 프로그램은 절차적 프로그램(procedural program) 또는 명령형 프로그램(Imperative program)에 속합니다.

C 프로그램에는 수많은 명령문이 포함되어 있으며, 이 명령문은 처음부터 끝까지 순서대로 실행됩니다.

따라서 원하는 결과를 얻기 위해서는 프로그램의 이러한 순차적인 흐름을 제어해야만 합니다.

 

이때 사용하는 명령문을 제어문이라고 하며, 이러한 제어문에는 조건문, 반복문 등이 있습니다.

이러한 제어문에 속하는 명령문들은 중괄호({})로 둘러싸여 있으며, 이러한 중괄호 영역을 블록(block)이라고 합니다.


조건문(conditional statements)

조건문은 주어진 조건식의 결과에 따라 별도의 명령을 수행하도록 제어하는 명령문입니다.

조건문 중에서도 가장 기본이 되는 명령문은 바로 if 문입니다.

 

C언어에서 사용하는 대표적인 조건문의 형태는 다음과 같습니다.


if 문

if란?

if 문은 조건식의 결과가 참(true)이면 주어진 명령문을 실행하며, 거짓(false)이면 아무것도 실행하지 않습니다.

 

if 문을 순서도로 표현하면 다음 그림과 같습니다.

 

 

C언어에서 if 문의 문법은 다음과 같습니다.

if (조건식) 

{

    조건식의 결과가 참일 때 실행하고자 하는 명령문;

}

 

위의 코드에서 블록에 속한 명령문은 중괄호({ })를 기준으로 오른쪽으로 들여쓰기가 되어 있는 것을 볼 수 있습니다.

이처럼 들여쓰기를 통해 코드의 가독성을 높이는 것을 인덴트(indent)라고 하며, 될 수 있으면 모든 코드를 인덴트하는 것이 좋습니다.

 


getchar()

  • getchar()란 scanf()와 비슷하게, 입력으로 부터 다음 문자를 리턴하는 함수임.
  • 하시만 getchar()는 어떠한 문장을 입력하든지 단 버퍼의 가장 앞에 입력한 단하나의 글자만을 입력받음.
  • 이를 언제 이용하는지 궁금한 바바리안이 많을 것이다! 
  • scanf를 사용하게 되면, 입력을 마치는 신호로 enter를 입력하게 되는데, enter값은 개행문자이며, 그 개행문자는 쓰레기 값으로 버퍼에 남는거임.(버퍼란 입력한 값을 저장하는 메모리다.)
  • 이 버퍼에 남은 쓰레기 값은 다음 입력함수를 사용시, 자동으로 입력되어, 다음 입력을 무시하게됨.
  • getcar()는 이 쓰레기 값인 개행문자를 입력받아 버퍼에 남은 쓰레기 값을 처리하는 너굴맨임!

 

  • 이는 getchar()의 사용방법을 보여 주는 예시이다.
  • 08번 라인에서 쓰레기 값이 이렇게 남는다.

  • 개행문자를 제외한 입력 값은 num으로 저장되버리고, 버퍼에 개행문자만 쓸쓸히 남아 있는 것이다!
  • 분노한 개행문자는 미친듯이 버퍼에서 나가고 싶다! 이대로 두면 다음 입력을 해야할때 버퍼의 문이 열린 틈을 타 이 개행문자가 튀어나와 우리의 입력은 무산 되버릴 것이다!! 그때 우리의 너굴맨 getchar()가 등장한다.!
  • 09번 라인의 getchar()는 분노한 개행문자가 버퍼에서 튀어나올 구멍을 만들어 준다!
  • 그 뒤의 11라인의 getchar()는 단 문자를 입력하는 용도로 사용된다.
  • 만약 09라인의 getchar()가 없었다면, 11라인의 getchar는 버퍼를 열자마자 남아있던 개행문자의 분노를 감당하지 못해 입력 받지 못 했을 것이다!! 

남아있는 \n은 너굴맨이 처리했으니 안심하라구~!

purchar()

  • putchar() 함수는 pirntf()랑 똑같은 친군데,  하나의 정수형 인수를 받아 하나의 문자로 출력하는 함수임.
  • putchar('\n') 로 한줄 띄우기도 가능함.
#include <stdio.h>

int main ()
{
   char c;

   printf("문자입력해주세요: ");

   c = getchar();

   printf("입력된문자는: %c \n",c);
   putchar(c);
   putchar('\n');

   return(0);
  • getchar하고 putchar하면 한줄 띄기가 됨! 
  • 이정도만 하고 넘어가도 됨!

if문에 else붙이기

  • if는 조건검사시, 값이 참일 때 다음 문장을 실행하는 함수다.
  • 거짓일 때는 발동하지 않는다.
  • 하지만 거짓일 때의 값을 출력하고 싶다면 if와 함께 else를 써주면 된다.
  • 우리도 고블린 부족을 털때, 시간이 부족하면 골드를 털지, 엘릭서를 털지 선택할 때가 있다!
  • 그 두개의 선택지를 주는 것이 else인 것이다!
//cypher2.c —알파벳 문자가 아닌 것들은 유지하고, 입력을 변경한다.
#include<stdio.h>
#include<ctype.h>  //isalpha()함수를 위해
int main(void)
{
    char ch;

    while ((ch = getchar()) != '\n')
    {
        if(isalpha(ch))     //문자이면 이 함수는 아스키 코드 값이 알파벳 문자인지 아닌지 구별한다.
            putchar(ch +1); //다음 문자를 출력한다.
        else                //그렇지 않으면
            putchar(ch);    //변경하지 않고 그대로 출력한다.
    }
    putchar(ch);            //개행을 출력한다.

    return 0;
}
  • 위의 예시를 본다면, while문 안에 if를 쓴 문장이다.
  • while반복문의 조건은 getchar()를 ch에 대입하고, 입력받은 값의 아스키 코드가 개행이 아니라면 계속 반복한다는 뜻이다.
  • isalpha는 아스키 코드값이고 이게 참이라면, 다음 문자를 출력한다.
  • 그렇지 않을 경우의 값을 출력하고 싶을 때 우린 else를 쓰는거다!!
  • 개념은 쉽다!

다수에서의 선택 else if

  • if와 else만으로 부족한 때가 있다. 인생은 양자택일만 있는 것이 아니다!
  • 많은 다수의 선택지가 주어졌을 때, 우리는 else if를 써서 더 많은 상황을 대처할 수 있다.!
  • 바로 예제로 알아본다!
// electric.c --전기요금을 계산한다.
#include <stdio.h>
#define RATE1   0.13230     //처음 360kwh까지 적용하는 비율
#define RATE2   0.15040     //그 다음 108kwh까지 적용하는 요율
#define RATE3   0.30025     //그 다음 252kwh까지 적용하는 요율
#define RATE4   0.34025     //720kwh를 초과할 때 적용하는 요율
#define BREAK1  360.0       //요율의 첫 번째 구분점
#define BREAK2  468.0       //요율의 두 번째 구분점
#define BREAK3  720.0       //요율의 세 번째 구분점
#define BASE1   (RATE1 * BREAK1)
                            //360 kwh에 대한 요금
#define BASE2   (BASE1 + (RATE2 * (BREAK2-BREAK1)))
                            //468 kwh에 대한 요금
#define BASE3   (BASE1 + BASE2 + (RATE3*(BREAK3-BREAK2)))
                            //720 kwh에 대한 요금
int main(void)
{
    double kwh;             //사용한 전력량
    double bill;            //부과할 전기요금

    printf("사용한 전력량을 입력하시오.\n");
    scanf("%lf", &kwh);     //double형을 위해 %lf사용
    if(kwh <= BREAK1)       //사용한 전력량이 360 이하
        bill = RATE1 * kwh;
    else if(kwh <= BREAK2)  //사용한 전력량이 360과 468사이
        bill = BASE1+(RATE2 * (kwh-BREAK1));
    else if(kwh<=BREAK3)    //사용한 전력량이 468과 720사이
        bill = BASE2 + (RATE3 * (kwh-BREAK2));
    else
        bill = BASE3 + (RATE4 * (kwh-BREAK3));
    printf("%.1f kwh의 전기요금은 $%1.2f입니다.\n",kwh,bill);

    return 0;
}
  • 예제가 길다고 겁먹을 필요 없다!
  • 저 위의 것들은 전부 조건문을 작성 할 때 필요한 상수값을 지정한 것일 뿐이다!
  • 첫번째 if는 사용한 전력량이 360kw이하 일때 값을 구하는 조건문이다!
  • 두번째 else if는 360과 468사이일때, 세번째는 468과 720일때, 나머지일 때
  • 이렇게 각각 다른 조건일 때 다른 값을 도출해야하는 상황에서 우리는 else if를 쓴다!

더 많이 중첩된 if

  • if를 두개 쓰고 else를 쓰면 어떻게 될까?
  • else는 바로 위의 if와 묶이게 된다!
//divisors.c-- 중첩된 if문들이 어떤 수의 약수들을 출력한다.
#include<stdio.h>
#include<stdbool.h>
int main(void)
{
    unsigned long num;      //검사를 위해 주어지는 수
    unsigned long div;      //잠정적인 약수
    bool isPrime;           //소수 플래그

    printf("검사할 정수를 하나 입력하시오");
    printf("(끝내려면 q)\n");
    while (scanf("%lu", &num)==1)
    {
        for(div =2, isPrime= true; (div *div)<= num; div=div+1)
        {
            if (num % div == 0)
            {
                if((div * div) != num)            
            printf("%lu,%lu: 둘다 %lu의 약수다.\n", div, num / div, num );
            else
                printf("%lu: %lu의 약수다.\n", div, num);
            isPrime = false; //소수가 아니다
            }
        }
       if(isPrime)
            printf("%lu: 소수다.\n", num);
        printf("검사할 또 다른 정수를 하나 입력하시오.");
        printf("(끝내려면 q)\n");
    }
    printf("안녕!\n");

    return 0;    
}
  • 때문에 여러개의 if를 쓰고나서 else를 쓰려면 {}를 사용해서 묶어주는거다!

논리연산자

간단하게 말하면 조건을 좀 더 논리적으로 연산 할 수 있게 도와주는 것이라고 생각하면 된다.

//chcount.c--논리 연산자 AND를 사용한다.
#include<stdio.h>
#define PERIOD '.'  //등호만 안넣고 표시는 함.
int main(void)
{
    int ch;
    int charcount=0;

    while((ch=getchar()) != PERIOD)
    {
        if(ch != '"' && ch != '\'')
        charcount=charcount +1;

    }
    printf("따옴표는 빼고, 문자%d개가 있습니다.\n", charcount);

    return 0;
}
  • 다음 예시를 보면 if문 조건에 ch != ' " ' && ch != ' \' ')라고 표시되어 있다.
  • 이 조건은 ch != ' " ' 와 ' && ch != ' \' 모두 충족을 시켜야만 참이 된다는 뜻이다.
  • ch != ' " ' 와 ' || ch != ' \' 은 둘중 하나만 충족 시키면 참이 된다는 뜻이다.
  • ch != ' " '에서 ' ! ' 는 ' = '앞에 쓰였는데, ' = '는 같다는 의미이므로, ' != '는 같지 않다는 의미이다.

Break

먼저 무한 루프에서 숫자를 증가시키다가 100이 나오면 반복문을 끝내도록 만들어보겠습니다. 다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

#include <stdio.h>

int main()
{
    int num1 = 0;

    while (1)   // 무한 루프
    {
        num1++;  // num1을 1씩 증가시킴

        printf("%d\n", num1);

        if (num1 == 100)    // num1이 100일 때
            break;          // 반복문을 끝냄. while의 제어흐름을 벗어남
    }

    return 0;
}
... (생략)
96
97
98
99
100

while 1을 지정하여 무한 루프를 만들고 그 안에서 num1을 1씩 증가시키고 if를 이용하여 num1이 100이 될 때 break를 실행합니다. 이렇게 하면 무한 루프라도 1부터 100까지만 화면에 출력합니다. 즉, 반복문 안에서 break를 실행하면 반복문은 바로 끝납니다.

while(do while)뿐만 아니라 for에서도 break의 동작은 같습니다.

#include <stdio.h>

int main()
{
    int num1 = 0;

    for (;;)    // 무한 루프
    {
        num1++;  // num1을 1씩 증가시킴

        printf("%d\n", num1);

        if (num1 == 100)    // num1이 100일 때
            break;          // 반복문을 끝냄. for의 제어흐름을 벗어남
    }

    return 0;
}
... (생략)
96
97
98
99
100

여기서는 무한 루프를 예로 들었지만 반복 횟수가 정해져 있더라도 break를 사용하면 반복문은 바로 끝납니다. 다음은 반복문과 break의 동작을 나타낸 순서도 입니다.

그림 30‑2 break로 반복문 끝내기


continue

이번에는 continue를 사용하여 일부 코드를 실행하지 않고 건너뛰어 보겠습니다. 다음은 1부터 100까지 숫자 중 짝수만 출력합니다.

#include <stdio.h>

int main()
{
    for (int i = 1; i <= 100; i++)    // 1부터 100까지 증가하면서 100번 반복
    {
        if (i % 2 != 0)               // i를 2로 나누었을 때 나머지가 0이 아니면 홀수
            continue;                 // 아래 코드를 실행하지 않고 건너뜀

        printf("%d\n", i);
    }

    return 0;
}
... (생략)
92
94
96
98
100

for를 사용하여 1부터 100까지 반복합니다. 그리고 if를 사용하여 i가 홀수이면 continue를 실행합니다(i 2로 나누었을 때 나머지가 0이면 짝수 0이 아니면 홀수입니다). 마지막으로 printf를 사용하여 i의 값을 출력합니다.

i가 짝수이면 printf가 실행되어 숫자가 출력되고, 홀수이면 continue가 실행되어 printf를 실행하지 않습니다. 즉, 반복문 안에서 continue를 실행하면 continue 아래의 코드는 실행하지 않고 건너뛴 뒤 다음 반복을 시작합니다.

for뿐만 아니라 while(do while)에서도 continue의 동작은 같습니다.

#include <stdio.h>

int main()
{
    int i = 1;
    while (i <= 100)     // i가 100보다 작거나 같을 때 반복. 1부터 100까지 증가하면서 100번 반복
    {
        i++;             // i를 1씩 증가시킴
        if (i % 2 != 0)  // i를 2로 나누었을 때 나머지가 0이 아니면 홀수
            continue;    // 아래 코드를 실행하지 않고 건너뜀

        printf("%d\n", i);
    }

    return 0;
}
... (생략)
92
94
96
98
100

여기서는 반복 횟수를 정한 뒤 continue를 사용했지만 무한 루프에서 continue를 사용하면 짝수만 계속 출력될 뿐 반복문은 끝나지 않습니다.다음은 반복문과 continue의 동작을 나타낸 순서도입니다.

continue로 코드 실행 건너뛰기


switch문

지금까지 if 조건문과 조건식에 사용할 수 있는 연산자들을 알아보았습니다. 이번에는 switch 분기문으로 다양한 조건을 처리하는 방법을 알아보겠습니다.

if는 조건식이 참이거나 거짓일 때 두 가지만 처리할 수 있었습니다. else if를 사용하면 여러 조건을 처리할 수 있지만 매번 조건식을 지정해줘야 해서 다소 번거롭습니다. switch 분기문은 조건이 많아도 손쉽게 처리할 수 있습니다.

 그림 26‑1 switch 분기문의 기본 형태

switch 분기문은 항상 case와 함께 사용하는데 변수의 값이 case에 지정한 값과 일치하면 해당 코드를 실행하게 됩니다(단, case에는 조건식이나 변수를 지정할 수 없습니다). 그리고 아무 case에도 해당되지 않으면 default의 코드를 실행합니다(default는 생략할 수 있습니다).

앞에서 else if를 배울 때 음료수 자판기를 예로 들었죠? switch case로 음료수 자판기를 만들면 다음과 같은 모양이 됩니다.

switch (버튼)
{
case 1:    // 콜라 버튼
    콜라를 내보냄
    break;
case 2:    // 사이다 버튼
    사이다를 내보냄
    break;
case 3:    // 환타 버튼
    환타를 내보냄
    break;
default: 
    제공하지 않는 메뉴
    break;
}

이처럼 switch 분기문은 형식이 균일하며 처리해야 할 조건이 많을 때 사용합니다(정수형=음료수 캔, 각 숫자=음료수 종류).

 

  • 예제를 본다
//vowels.c --한 문장에 다수의 레이블을 사용한다
#include<stdio.h>
int main(void)
{
    char ch;
    int a_ct, e_ct, i_ct, o_ct, u_ct;

    a_ct = e_ct = i_ct = o_ct = u_ct = 0;

    printf("간단한 영문 텍스트를 입력하시오(끝내려면 #):\n");
    while((ch = getchar()) != '#')
    {
        switch(ch)
        {
            case'a' :
            case'A' : a_ct=a_ct+1;
                      break;

            case'e' :
            case'E' : e_ct=e_ct+1;
                      break;
            case'i' :
            case'I' : i_ct=i_ct+1;
                      break;
            case'o' : 
            case'O' : o_ct=o_ct+1;
                      break;
            case'u' : 
            case'U' : u_ct=u_ct+1;
                      break;
            default  : break;
        }                               //switch의 끝
    }                                   //while 루프의 끝
    printf("모음의 수:  A    E    I    O    U\n");
    printf("         %4d %4d %4d %4d %4d\n",a_ct,e_ct,i_ct,o_ct,u_ct);

    return 0;
}
  • 여기서 주의 해야하는 점은, 조건이 끝나면 무조건 break를 걸어줘야 한다.
  • 그렇지 않으면 그 아래 조건 까지 모두 실행하게 된다.