맨위로

C PROGRAMMING - 기초2012. 9. 23. 13:07

기간 2012.08.25 ~ 2012.09.23

안녕하세요 ? 이번 강의에서는 C언어의 제어흐름 에대해서 알아보도록 하겠습니다. 글을 시작하기전에 이블로그에 와주신 모든 분들께 감사의 말씀드립니다. 

제어흐름

C언어에서 제어흐름은 계산순서를 정하는것입니다. 앞장에서 살펴본것들도 C언어에 일반적인 제어흐름에 속하게됩니다. 하지만 앞에서 살펴본것들은 가볍게 살펴본것이고 이번글에서는 C언어 제어흐름에 대해서 자세히 배우게됩니다. 그럼일단 우리가 배워볼 제어흐름의 종류를 알아보겠습니다. 그종류는 다음과 같습니다.

  • If - Else 문   

  • Else - If 문

  • Switch 문

  • 순환문 - while 과 for

  • Do - while 루프

  • Break 와 Countinue

  • Goto 와 Label 

위에 나오는 If(만약 …이라면) , else(다른) , while( …하는 동안에) 와 같은 단어들은 여러분이 이미 알고있는 단어입니다. 여러분이 아는 단어의 의미가 C언어에서의 의미과 거의 비슷하여 이번장의 내용은 그리어렵게 느껴지진 않을거라 생각됩니다. 

이 제어흐름에 대해서 배우기 위해서는 문장과 블록에대해서 알필요가있습니다. 그러므로 본격적인 제어흐름을 공부하기전에 문장과 블록에 대해서 알아보고 진행하도록 하겠습니다.

문장과블록

C언어 코드중 다음과 같은 코드가 있습니다.

x = 0;
i++;
printf(……)

위의코드는 다음과 같은 형태로 쓰여져있습니다. 

수식 (expression) ; (세미콜론)

x = 0 , i++ , printf(………………) 와 같은 수식과 세미콜론으로 끝이나는 형태로 쓰여져있습니다. C언어에서는 세미콜론은 문장의끝을 나타내는 의미로 쓰이게 되는데 바로위의 코드에서 문장에 끝을 나타내고 있습니다. 따라서 수식에 세미콜론을 붙혀서 문장이되는것입니다. 그럼 다음코드를 보겠습니다.

if (n>0)                           /*  (n>0)  ,  (a>b)  는 수식 */

if (a>b)

z = a;                   /* z = a; , z=b; 는 문장 */

else

z = b;

위의문장에서 ( n > 0 ) 과 ( a > b ) 는 수식이되며 z = a ;와 z = b ; 는 문장이됩니다.

<알고갑시다 !><알고갑시다 !>

 x = 0 또는 i++ 와 같은 것을 수식(계산의 방법과 규칙을 문자나 기호를 써서 표현한 것)이라하며 수식이 세미콜론으로 끝나게 되면 문장이된다.

이제 블록에 대해서 알아보겠습니다. 블록은 중괄호{ }를 이용하여 만들며 여러개의 선언문이나 문장을 모아서 구성하게됩니다. 이렇게 묶인것은 단일 문장과 동일한 기능을합니다. 예를들면 함수의 시작과 끝에 쓰이는 중괄호for문이나 if문에서의 중괄호를 들수있습니다. 여기서는 블록끝에 세미콜론이 올수없음을 유의 해야합니다.


If - Else 문

if - else 문에서 단어if의 의미인 '만약 ~이라면' 과 else의 뜻인 '그렇지 않으면' 의 의미는 프로그래밍에서의 뜻과 별반다르지 않다. 그렇기 때문에 If - Else 문이 그리어렵게 느껴지지 않을것이다. 이 if - else 문은 프로그램의 흐름을 결정하기위해 쓰입니다. 형태는 다음과 같습니다. 

if     (수식)
문장1                      /*   수식;   */      
else
문장2

if - else 문을 사용한 코드를 살펴 보겠다. (약간의 생략)

int main (void)

{

int n = 10;

if (n>5)

printf("5보다크다.");

else

printf("5보다 크지않다.");

………

}

위의코드에서 n>5 는 수식이며 제일먼저 계산된다. 여기서 수식값이 참이면 문장1을 실행시키고 참이아니면 문장2를 실행시키게 된다. 그러므로 위의수식은 참이되므로 문장1이 실행되어서 결과값은 다음과 같아진다.

5보다 크다.

이런 if - else 문에서는 필요에 따라 else 를 사용하지 않아도 된다. else의 사용이 선택적이기 때문에 사용할시에는 if문과 정확하게 연결되어야한다. 만약 다른 if와 연결되면 전혀 다른결과가 출력될수도 있다. 이런 불상사를 막기위해서는 어떻게 연결되는지 알아야한다. 다음코드를 보면서 알아보겠습니다.

if (n>0)

if (a>b)

z = a;

else

z = b;

위와 같이 일반적인 상황에서는 else와 가장가까운 if문과 연결되있다고 생각하면 된다. 하지만 두번째 if와 연결하려는 의도가 아니라면 다음과 같이 중괄호를 사용해준다.

if (n>0){

if (a>b)

z = a;

}

else 

z = b;

 <알고 갑시다!>

 if - else 문은 프로그램의 흐름을 결정하기 위해 사용됩니다. else의 사용은 필요에 따라 달라집니다. 일반적으로 if는 가장가까운 else와 연결되지만 중괄호를 적절히 사용하면 다른 if와 연결할수 있습니다.

아래는 else의 잘못된 사용의 예입니다. 아래와 같은 실수는 발견하기 매우어려우므로 작성시부터 주의를 기울여야 합니다.

if (n >= 0)

for (i=0; i<n; I++)

if (s[i] >0){

printf("……");

return i;

}

else       /*     잘못된 사용        */

printf("error --n is negative \n");

Else - if 문

Else - if 문의 형태는 다음과 같습니다.

if (수식1)

문장1

else if (수식2)

문장2

else if (수식3)

문장3

else

문장4

이 Else - If 문은 다중판단을 할경우 자주사용되는 형식입니다. Else - If 문은 위에서 부터 차레대로 계산됩니다. 먼저 수식1을 계산하고 참이면 문장1을 실행시키고 거짓이라면 수식2를 계산하고 참이면 문장2실행, 거짓이면 다음수식으로 넘어가는 과정을 반복합니다. 이런식으로 반복해서  하나도 참인것이 없다면 마지막 else의 문장을 실행시키게 됩니다. 이 부분은 실수를 검사하기 위해 사용되기도 하지만 필요가 없으면 문장을 생략할수 있습니다.

Switch 문

이번에 알아볼 switch 문도 앞에서 배웠던 if - else 문과 마찬가지로 흐름을 분기시키는 역활을 한다. 그러나 if - else 보다 간결하게 표현된다는 장점으로 인해 자주사용된다. switch 문은 일반적으로 다음과 같은 형태이다. 

switch (수식) {
case 상수 : 문장
case 상수 : 문장
default : 문장
}

switch 문의 상수수식과 상수는 정수값을 가져야 합니다. 만일 case의 상수값이 수식과 일치하면 그뒤에 문장을 실행합니다. case 다음에 상수들은 모두 달라야 하며 똑같은 수식은 2번이상 사용하면 안됩니다. default 문은 모든 case에 만족되는 것이 없을시 실행되며 필요가 없을시 생략해도 됩니다. 예제코드를 보시면 무슨말인지 알수있을것입니다. 예제 코드는 다음과 같습니다. 

switch(n)
{
case 1:                             /*     n이 1이면 여기서 부터 시작  */
printf("A1 ");
printf("A2 ");
case 2:                             /*     n이 2이면 여기서 부터 시작  */
printf("B1 ");
printf("B2 ");
case 3:                             /*     n이 3이면 여기서 부터 시작  */
printf("C1 ");
printf("C2 ");
default;                             /*     n에 해당하는것이 없으면 여기서 부터 시작   */
printf("Default!");
}

위의코드를 보면 n의 값에 따라 case 가 달라지고있다. (여기서 n의 값은 무조건 정수값을 가져야한다. 실수 값은 올수없습니다. *한번 해보는것도 좋은방법입니다.)
만약 n이 2라면 다음과 같은 의미가된다. 

"n이 2라면 case2 부터 시작된다. "
출력결과 - B1 B2 C1 C2 Default!

만약 n이 3이라면 다음과 같은 의미가 된다.

"n이 3이라면 case3 부터 시작된다. "
출력결과 - C1 C2 Default!

만약 n이 1,2,3 외의 다른경우라면 다음과 같다.

"n이 1,2,3 외의 다른경우라면 Default 부터시작"

출력결과 - Default !

실행결과 case와 일치하는것을 시작으로 차례차례 수행하는것을 알수 있다. 만약 case뒤에 다른 case들을 실행하기 원치 않는 다면 switch 문과 break문을 사용하면된다. break문은 특정조건일때 반복문을 빠져나오는데 사용됩니다. 사용법은 간단흠으로 다음코드를 보면 금방알수 있다. 

switch(num)
{
case 1:                           
printf("A1 ");                       case 1
printf("A2 ");                         영역
break;                                               
case 2:                          
printf("B1 ");                       case 2 
printf("B2 ");                         영역
break;                                              
case 3:                            
printf("C1 ");                        case 3
printf("C2 ");                          영역
break;                                               
default;                          
printf("Default!");
}

위의코드를 보면알수있듯이 case 에 마지막에 break; 만 써주면 됩니다. 그렇면 실행결과를 보겠습니다.

num 이 1일때 실행결과  -   A1 A2

num 이 2일때 실행결과  -   B1 B2

num 이 5일때 실행결과  -   Default!

순환문 - while 과 For

먼저 while 의 기본형태를 보겠습니다.
수식1;
while (수식2)
{
문장
수식3;
}

위의 while 문은 먼저수식을 계산하고 0이아니면(참인동안)문장을 수행하고, 수식을 다시계산하여 수식이 0이 될때(거짓)까지 반복하게됩니다. 이런 특성으로 인해 while은 반복문이라고 합니다. 그러면 while 문이 사용된 예제를 하나 보겠습니다. 

#include <stdio.h>

int main(void)

{

int i = 0;

while (i<7)     /* 7회 반복   */

{

printf("%d 번째 Hello World ! \n",i+1);

i++;

}

return 0;

}

위의 예제는 Hello World ! 를 7번 출력하는 반복문입니다. 위예제를 보면 Hello World 가 한번 출력할때 마다 i에 값에 +1을 하여 반복을 하는 구조입니다. 만약 여기서 i++가 없다면 어떻게 될지 생각해보자. 그결과는 while의 조건이 항상참이므로 멈추지 않고 계속 반복할것이다. 이렇게 빠져나오지 못하도록 구성된 반복문을 '무한 루프' 라고 한다.

이제 while의 기본형태를 보았으니 For문의 기본형태를 보겠다. for문은 다음과 같다.

for (수식1; 수식2; 수식3)

문장

이것을 while 로 표현 하면 다음과 같다.

수식1;
while (수식2)
{
문장
수식3;
}

위처럼 for문이 하는것은 모두 while 문으로 표현할수 있다. 하지만 for문이 쓰일곳에는 for문을 쓰는것이 더간결하고 좋은코드가 된다. 다시본론으로 돌아와서 for문에 대해서 알아보겠습니다. 

for문도 while문과 마찬가지로 반복문입니다. 반복문에는 기본구성3요소가 있는데 그요소는 다음과 같습니다.

              1. 반복횟수를 세기 위한 변수         
              2. 반복문의 탈출조건                
              3. 탈출조건 성립을 위한 연산        

이 기본구성3요소를 같은 반복문인 while 문의 예제에서 찾아보면 다음과 같습니다.

#include <stdio.h>

int main(void)

{

int i = 0;    ①

while (i<7)     /* 7회 반복   */

{

printf("%d 번째 Hello World ! \n",i+1);

i++;       ③         

}

return 0;

}

위의 while문을 보면 반복문의 기본요소가 흩어져있어서 관찰하는데 불편함이있습니다. 그래서 이런 불편을 줄이고자 For문이 만들어졌습니다. 불편을 줄이고자 만들어진 for문은 반복문의 기본구성  3요소를 한줄에 나열하는 구조로 되어있습니다. 그러면 위의 3요소를 For문을 써서 작성해보겠습니다. 그코드는 다음과 같습니다.

for (int i=0; i < 7; i++)

{

printf("………")

}

일단 for문이 어떤형태인지 알았으니 흐름에 대해서 알아보겠다. 흐름을 알기 위해서는 위코드와 조금달라진 코드를 사용한다. 그코드는 다음과 같다.

for (int i=0; i < 3; i++)           /*      int i=0  -①    */

{                                     /*       i < 3    -②    */

printf("………")              /*         printf("………")       -③       */

}                                     /*                i++          -④             */


<흐름>

첫번째 루프  ①→②→③→④    i=1

두번재 루프  ②→③→④          i=2

세번째 루프  ②→③→④         i=3

네번재 루프  ②                     i=3 end

여기서 주목할점은 ①번은 For문 처음에만 실행되며 ④번은 반복영역을 실행하고 마지막에 실행된다는점이다. 특히 ④번은 주의하기 바란다. 위의 번호에도 이름이 붙혀져 있는데 ①은 초기화하기 때문에 초기식 ②에는 반복을 위한 조건이오기 때문에 조건식 ④에는 탈출을 위한 연산식인 증가나 감소가 오므로 증감식이라 한다. 이름으로 정리하면 다음과 같다.

for (초기식; 조건식; 증감식)

{

for문의 몸체

}

 <알고 갑시다!>

 위의 예제중 for문에 초기식에 변수선언이 올경우 표준에는 맞지만 대부분의 C컴파일러가 반영하고 있지 않으므로 변수선언을 먼저해줘야 한다. 

for (int i=0;i<3; i++)  int i; for (i=0; i <3; i++)

이 for문도 필요한것만 쓰고 필요없는 것은 빼도 된다. 초기식,증감식도 생략가능하다 단 , 조건식이 빠지면 무한루프가 형성된다. 다음은 문자열뒤집기 함수에서의 for문 사용의 예이다. 한번살펴보기 바란다.

#include <string.h>

void reverse (char s[])

{

int c,i,j;

for (i=0,j=strlen(s) -1; i<j; i++,j--){

c = s[i];

s[i] = s[j];

s[j] = c;

}

}


Do - While 루프

앞에서 살펴본 while이나 for문은 종료조건검사를 앞부분에서 한다. 그러나 반복문을 구성하다보면 이러한 조건검사 방식이 적합하지 않은 경우가 있다. 즉 앞이 아닌 뒷부분에서 하는것이 더적합할수 있다. 이를 위해서 만들어진 구문이 바로 Do - while 문이다. 이런 Do - while 문은 종료조건을 마지막부분에서 검사하기 때문에 최소 한번은 수행된다는 특징이있다.(만약 조건이 만족되지 않으면 한번도 실행되지 않을수 있다.) 그럼 Do - while 문의 형식을 보겠다. 

do{
문장
}while(수식);

위의 do - while문을 보면 알수있듯이 문장이 실행되고 수식이 계산된다. 여기서 계산결과가 참이면 문장이 다시 수행되고 수식이 거짓이면 루프가 끝나게 된다. 이제 예제를 보겠습니다. 이예제는 do -while문의 특징이 잘드러나있는 예제입니다. 

#include<stdio.h>

int main(void)

{

int age;

do{

printf("당신의 나이는 ? ");

scanf("%d", &age);

} while(age<1);

switch(age/10)

{

case 0:

printf("유소션이구나! \n");

break;

case 1:

printf("10대군요. \n");

break;

case 2:

printf("20대군요. \n");

break;

case 3:

printf("30대군요. \n");

break;

case 4:

printf("40대군요. \n");

break;

case 5

printf("50대군요. \n");

break;

case default:

printf("어르신 ~ \n");

}

return 0;

}

실행결과  

당신의 나이는? -15

당신의 나이는? 0

당신의 나이는? 20

20대군요.

 <알고 갑시다!>

 while 문은 '반복'이라는 상징성을 띄고 do - while 문은 '실행후 조건검사' 라는 상징성을 지닌다. 만약 while문과 do - while 문 모두로 해결가능하다면 while문을 쓰기 바란다. do - while문을 쓴다면 프로그래머들에게는 그 코드가 '실행후 조건검사'가 적합한 상황이다. 라고 받아들여지기 때문이다.

Break 와 Continue

어떤경우에는 루프의 도중에서 벗어나야 할때가 있다. 이런경우 쓰는것이 break 문이다. 이 break 문은 switch문 뿐만아니라 for문 , while문 등에서 루프를 벗어나는 역활을 한다. 이 break 문은 가장가까운 루프하나를 벗어난다. 단,중첩된 루프일 경우에는 한번에 전체를 빠져나올수 없다. 다음예제를 보면서 알아보겠습니다. 다음은 공백과 탭등을 제거하는 trim 함수입니다. 

int trim(char [s])

{   

int n;

for (n = strlen(s)-1; n >= 0; n--)

if (s[n] !=' ' && s[n] != '\t' && s[n] != '\n')

break;

s[n+1] = '\0";

return n;

}


continue 문은 break 문과 반대로 for,while 또는 do 루프에서 다음반복을 수행하기 위해서 사용된다. 이 continue 문은 실행하던 반복문의 나머지 부분을 생략하고 프로그램의 흐름을 조건검사 부분으로 이동시킨다. 즉 while 문이나 do문의 비교부분이 다시 수행됨을 의미한다. 다음예를 보겠습니다. 다음예는 양의 원소만 처리되고 음의 원소는 건너뛰는것을 보여줍니다. 

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

if (a[i] < 0)

continue;


}

 <알고 갑시다!>

 continue 는 for문에서는 제어흐름을 넘기는것을 뜻합니다. 이 continue문은 루프안에서만 사용될수 있으며 switch 문에서는 사용될수 없습니다.


Goto 와 Label

goto는 c언어 뿐아니라 많은 언어에서 볼수있는 키워드이다. 그럼에도 불구하고 많은 개발자들은 사용하기를 꺼리고 사용하지말라고 권고한다. 물론 긍정적인 측면을 이야기 하는 전문가도 있다. 하지만 goto를 쓰게되면 코드도 복잡해지며 c언어의 절차지향적특성이 무너지므로 장점보다 단점이 크다. 이런 goto를 작은 장점때문에 쓸필요는 없다. 하지만 쓰지 않더라도 알고는 있어야한다. (물론 사용하지 않는 습관을 우선적으로 들여야 한다.) 

goto문은 말그대로 go + to 이며 기능도 말그대로 특정위치로 이동시키는 기능을 한다. 구성은 다음과 같다.

int main(void)
{
goto rabbit;           /* rabbit 으로 가라 */
     
puppy:                /*  Label   */
rabbit:                /*  Label   */
goto puppy;           /*   puppy 로 가라    */
…   
}

위의코드에서 rabbit 과 puppy를 레이블이라 한다. (레이블을 의미하려면 콜론으로 표시한다.) 위구성중 goto rabbit; 이라는 문장을 해석하면 "레이블 rabbit 으로 가서 계속실행하라 " 라는 의미가 된다. 이런 goto 문은 중첩된 루프를 벗어날 경우에 유용하다 그예는 다음과 같다. 

for (…)

for (…) {

if (disaster)

goto error;

}

error :

clean up the mess

이구조는 에러가 여러곳에 걸쳐발생할때 유용하다. 여기서 error를 레이블로 표시했다. 


마치며

글을 읽다가 이해에 어려운 부분이 있으시면 댓글로 질문해주시기 바랍니다. 다음글은 함수와 프로그램구조를 다루게 됩니다. 감사합니다.


'C PROGRAMMING - 기초' 카테고리의 다른 글

C언어 - 제어흐름  (0) 2012.09.23
C언어 - 자료형(Data Type)과 변수,연산자,수식 그리고 상수  (1) 2012.08.25
C언어 - 외부변수  (0) 2012.06.07
C언어 - 문자 배열  (0) 2012.05.26
C언어 - 매개변수값 전달  (0) 2012.04.22
C언어 - 함수  (0) 2012.04.21

Posted by STIH

댓글을 달아 주세요