2005년 5월 30일 월요일

[펌] 프로그래밍 구조(순서도,건너뛰기)

제 6 장 : 프로그래밍 구조(순서도,건너뛰기)

*강의개요 : 순서도와 C언어 프로그래밍 구조

*학습목표 : 모듈화 프로그래밍의 기본을 배운다.

*요점정리 :

순서도를 이용하여 순차구조,분기구조,건너뜀구조,반복구조등을 설명한다. 또한 이런 구조의 C언어 코딩은 어떻게 되는가를 이해한다.

*하고픈 말:

벽돌 쌓는 기술이 좋고 미장기술이 좋다 하여도 설계도 없이 집을 지으면 쉽게 무너진다. 같은 의미로 프로그래밍 설계 없이 코딩하는 것은 모래위에 성을쌓는것과 같다.


지금까지 C언어의 기본 문법들을 공부 했었습니다.본장에서는 C언어에 대한 내용보다는 소프트웨어공학에 해당하는 부분입니다.소프트웨어 공학이란 프로그램 설계를 어떻게 하면 잘하는가를 공부하는 학문입니다.  “C언어 하다 말고 웬 소프트웨어 학문인가”라고 하실수 있을것입니다.

필자가 지금까지 프로그래밍을 해왔을때 느낀 점은 “코딩보다 중요한 것이 설계이다 ”라는 것입니다. 문법을 알고 코딩을 해도 그 흐름이나 구조가 체계적이지 않으면 프로그램이 최적화 되지 않습니다.

결국 설계가 중요하다는 것을 강조하고 싶습니다. 건물을 지을때 기둥을 만들고 벽돌을 쌓는 것은 매우 중요합니다. 그러나 그이전에 어떻게 집을 지을것인가의 설계가 중요합니다. 같은 의미로 프로그래밍을 할때 코딩도 중요하지만 어떻게 프로그램을 구축할것인가? 설계가 중요한것입니다.  이런 설계의 기본이 순서도(flow chart)입니다. 순서도는 모듈을 어떻게 설계할것인가를 제안해주는 아주 중요한 부분입니다. 순서도를 보고 이것을 C언어로 어떻게 구현을 하는가를 알수 있다면 매우 쉽게 프로그래밍을 할 수가 있습니다. 본장에서는 C언어와 순서도와의 연계 방법 및 구현 방법에 대해서 설명합니다.


6-1.프로그램의 기본 제어 구조

기본 제어 구조에는 4가지가 있습니다.

1) 순차구조(sequence structure), 명령문이 나열된 순서대로 하나씩 순차적으로 수행한다.

2) 선택구조(selection 또는 decision structure), 조건분기 if else 같은 것.

3) 반복구조(repetition, iteration, loop structure), 조건에 따라 반복하는 것. ex) while, for,

    do-while 같은 것.

4) 건너뜀 구조(jump structure), 프로그램의 흐름을 도중에 건너뛴다. ex) goto, break, continue

이런 네 개의 구조가 사실 기본제어구조입니다. 전장에도 이런 구조에 대한 기본을 배웠습니다. 그럼, 이런 구조들을 어떻게 연결을 할 것인가? 하는 부분들을 설명합니다. 이것을 배운 후에 여러분들이 프로그램들을 만들게 될 때에는, 아주 간단한 프로그램이라 하더라도 이런 제어구조대로 설계를 끝내고 프로그래밍에 들어가는 연습을 하기를 바랍니다. 많은 사람들은 설계 없이 프로그램을 만듭니다. 만약 설계를 하지 않고 프로그램을 하게 되면 프로그램을 다 만든 다음에 다시 그걸 바꾸려고 할때 매우 어려움에 닥치게 됩니다. 소스를 보게되면  머리 아프고, 다시 처다 보고 싶지 않고, 내가 짠 프로그램이지만,  내가 짠 건지 안 짠 건지 감이 잡히지 않을 정도로 정신이 없게 되는 그런 경우가 됩니다. 아무리 긴 라인의 소스라고 하더라도 설계를 먼저하고 그 설계에 입각해서 코딩을 하게 된다면 버그가 발견되어도 어느 부분에서 버그가 있는지를 확인할수 있고 쉽게 버그를 고칠수도 있습니다. 또한 프로그램을 업그레이드 할경우에도 설계가 탄탄하게 되어 있다면 쉽게 할수 있습니다. 결국 그 의미는  구조화와 설계화가 프로그램 제작에 매우 중요한 관계가 있다는 것입니다. 여러분들은 이번 장을 굉장히 중요시 생각해야 합니다. C 언어는 객체화, 모듈화 프로그램입니다 객체화, 모듈화 므로그램밍에서는 data flow chart를 많이 사용합니다. 그래서 순서도로 기초적인 프로그래밍을 설계하는 연습부터 배워 나가고, 그 다음에 좀 더 어려운 프로그램을 배워보자 하는 것이 본 장의 취지가 되는 것이다. 필자의 말을 그냥 지나치면 안됩니다.  설계가 없이 무조건 만들어서 어떤 프로그램을 만들었다면 이것을 1.0으로 보지말고, 그것을 데모 버전으로 생각하는 것을 권장합니다. 그리고 다시 한번 제작하시기 바랍니다.. 먼저 설계도를 작성하고 그 설계도에 맞추어서 다시 한번 만드시는 것입니다. 그러면 정말 최적화된 프로그램을 작성할수 있습니다. 왜냐하면, 프로그래밍을 할 때 여러분들이 해본 프로그래밍을 다시 하는 경우는 별로 없고, 새로운 프로그래밍을 하는 경우가 많은데 새로운 프로그래밍 일 경우에는 내가 안 해 봤던 것을 하는 경우가 많습니다. 그래서 프로그램밍을 하다보면 어려운 점들을 부딪히게 되고,  그것을 피해 나가려고 jump, if else 등을 써서 억지로 프로그램을 돌게 만듭니다. 돌게 만들고 난 다음에야 ‘아, 이 프로그램을 어떻게 짜야 되는구나!’ 그걸 알게 되는 것이지요. 그다음이 중요합니다. 그렇게 한 다음에 다시 설계해서 새로 짜보시라는 것입니다. 그렇게 해야만 제대로 된 프로그램이 나온다는 것입니다 한 개의 프로그램을 만들어도 재사용 할 수 있게끔 만든다는 것이 굉장히 중요한 것입니다. 그렇게 하면 할수록 자기가 만들어 놓은 프로그램을 계속 재생하니까 힘이 늘어나고, 실력이 늘어난 다는 것을 꼭 기억해 주기를 바랍니다. 오늘 배우는 이 네 가지의 구조들이 어떤 것인가를 깊이 생각하고 그것을 순서도화하고 설계하는 기법을 꼭 배워두시길 바랍니다.


6-2.순서도


그림 6-1)이 순서도(flowchart) 기호입니다.

그림 6-1 순서도 기호


타원형 형태를 단말(terminal)이라고 합니다. 이 기호가 프로그램의 시작과 끝을 알리는 기호입니다. 알고리즘의 시작과 종료를 의미하는데 ‘void main()’이 바로 이 부분에 속합니다. 또는 { } 의 처음을 의미하기도 합니다.

사각형 형태를 처리(process)라고 합니다. 연산작업, 자료처리라는 것인데 우리가 배웠던 사칙연산, 논리연산, 집합연산 같은 것입니다. 연산을 처리하는 부분들이 바로 사각형 process 안에 들어갑니다.

다이아몬드 형태는 조건분기, 선택, 판단 이런 것들을 표시하는 기호 입니다. 즉 if, switch문을 사용할 때는 바로 이 선택 (selection)이라는 기호를 사용하게 됩니다.

육각형 형태는 반복(repetition)이라고 하는데, while, do-while, for문을 사용할 때 이 기호를 사용합니다.

화살표는 흐름선을 의미합니다. 데이터의 흐름방향을 표시하는 것이지요.

flowchart에 여러 가지 기호가 있는데, 기본적으로 여러분들이 외워야 될 기호들은 위에 있는 것들입니다. 웬만하면 이것들만 가지고 순서도를 만들어 보는 것이 좋습니다. 그러면 이제부터 지금까지 우리가 배웠던 흐름제어 부분들에 대하여 한번 순서도를 만들어 보겠습니다. 그래서 순서도가 어떻게 만들어지는 것인가 그것을 확인해 보고, 그리고 이 순서도와 언어 프로그램 관계는 어떻게 연동이 되는가? 하는 것을 배워보겠습니다.


6-3.순차구조와 분기구조

순차구조란 한스텝 한스텝씩 차례대로 수행하는 구조를 의미합니다. 분기구조는 조건에 따라서 서로 다른 항목을 수행하는 부분입니다. 이 두개의 구조는 프로그래밍 설계에 기본 구조입니다.

6-3-1 순차 구조

그림 6-2 순차구조


순차구조는 처리하는 방법이 하나하나 순서적으로 처리되는 것을 말합니다. 순차구조는 일방 처리구조라고 볼 수 있습니다. 일반적으로 프로그램을 짤 때 step by step으로, 한 step, 한 step. step by step으로 가는 것을 sequence structure. 즉, 순차구조라고 이야기합니다. 별것 아닌 것 같지만, 이것은 굉장히 중요한 구조입니다.  그림 6-2에서 보듯이 Command1 다음에 Command2를 순서적으로 수행하는 것을 의미합니다. 프로그래밍에 가장 기본적이 구조라고 볼수 있습니다.


6-3-2 분기구조

분기 구조는 조건에 따라서 서로 다른 항목들을 수행하는 부분을 의미합니다. 그림 6-3은 분기구조를 보여줍니다.

그림 6-3 분기구조


마름모로 되어 있는 곳에서 비교를 해서 맞으면(Yes) command2를 실행하고, 비교를 해서 틀리면 (No) command1을 실행합니다. 그림6-3을 소스로 다시 작성해 보면,


void main()

{

   if(comp)

   {

     command2;

   }

   else

   {

      command1;

   }

}

과 같이 쓸 수 있습니다.  if를해서 comp라는 (compare의 약자) 조건에 맞으면 command2를 실행하고, 이 조건에 맞지 않으면 command1을 실행하라는 의미가 됩니다. 순서도 하나, 하나를 이렇게 C언어로 옮겨주고, C언어와 순서도를 동시에 그림으로 느낄 수 있는 버릇들을 들이게 되면 앞으로 프로그램밍을 하시는데 많은 도움이 되시리라고 생각합니다. 여러분들은 앞으로 어떤 프로그램 구조를 순서도로 만들고 나서 ‘이것이 코딩이 되면 실제 소스는 이렇게 될 것이다.‘라고 느껴져야만 됩니다. 객체지향(Object-Oriented Programming) 프로그램에서 순서도가 필요 없을 것 같다고 생각하는 사람들도 있을 것입니다. 하지만 그것은 위험한 생각입이다. 객체지향 프로그램에서는 모듈 안에 순서도들은 항상 포함됩니다. 그래서, 그 객체의 메소드(객체의 안에 구동되는 내용)들은 순서도로 설명하는 것이 가장 좋습니다. 다시 한번 강조하지만, 순서도와 소스를 동시에 머릿속으로 그릴 수 있는 연습을 하도록 노력하시기 바랍니다.


6-3-3.다중분기


그림 6-4는 다중 분기를 보여줍니다.

그림 6-4 다중 분기 구조


start terminal에서부터 비교(comp)를 해서 case1일 경우, case2일 경우, case3일 경우를 (이때는 원래 마름모꼴을 쓰지 않고 다른 기호를 쓰기도 하지만 중요한 것은 이렇게 봐도 알아 볼 수도 있으니까 아까 말했던 5가지 기호만 가지고 설명을 드리는 것입니다.) switch문을 이용해서 case1일 때는 command1을 실행하고, case2일 경우에는 command2를 실행하고, case3일 경우 command3을 실행하라는 의미입니다. 이것을 소스로 나타내 보면,


void main()

{

  switch(comp)

  {

    case1;

          command1;

          break;

    case2;

          command2;

          break;

    case3;

          command3;

          break;

  }

}

과 같이 됩니다.


이렇게 순서도를 보면서, 이런 소스가 머릿속에 동시에 떠오르십니까? 아직 안되었더라도 걱정하지 마세요. 시간은 충분히 있습니다. 연습을 하면서도 틀리더라도 될 수 있으면 책을 안보고 하는 것이 좋습니다. 그래야 프로그래밍 구조를 배울 수가 있고, 그렇게 그려진 구조대로 프로그래밍을 할 수가 있습니다. 물론, 처음부터 이렇게 되는 것은 아니지만, 자꾸 양쪽으로 생각하려고 노력하면서 보면 나도 모르게 어느 샌가 그런 구조화적인 프로그램의 개념을 느낄 수 있으실 것입니다.


6-4.반복

그림 6-5와 그림 6-6은 두가지 종류의 반복구조를 보여줍니다.

     

그림 6-5 선판단 반복               그림 6-6후판단반복


그림 6-5) 와 그림 6-6)을 가만히 살펴보면, 아주 유사하다고 생각이 들 것입니다. 틀린점이 하나 있다면, 비교하는 부분이 앞에 있다는 것과 뒤에 있다는 것이 다르다는 것이 다릅니다. 그림 6-5)을 설명해 보면 start를 한 다음 비교(comp)를 하는데, 맞으면 command1, command2로 갑니다. 이렇게 계속 looping을 하다가 비교값이 틀릴 때 command3로 빠져나가서 프로그램이 끝나게 되는 것입니다. 이런 순서도를 쓰는 반복문은 'while'문과 ‘for'문입니다. 이것들을 소스로 나타내 보면,


*while문*

void main()

{

  while(comp)

  {

    command1;

    command2;

  }

  command3;

}


*for문*

void main()

{

  for(i=0 ; i<N ; i++)

  {

     command1;

     command2;

  }

  command3;

}


과 같이 쓸 수가 있습니다.


그림 6-6)을 보면 일단 무조건 한번은 수행은 하고 나서 비교를 하는 구조입니다. 그래서 start를 하고 바로 command1과 command2를 수행하면, 비교구문이 나오는데, 이때 비교가 맞으면 다시 command1으로 올라가고, 맞지 않으면, command3로 빠져 나오게 되는 것입니다. 이런 구조를 쓰는 대표적인 구문은 'do-while'입니다. 이것 역시 소스로 나타내보면,


void main()

{

  do

  {

    command1;

    command2;

  }while(comp);


  command3;

}


과 같이 쓸 수가 있습니다. 알아두어야 할 점은 while문과 for문은 data가 흐르는 것은 비슷하지만, 어떤 용도에 맞추어서 쓸 것인가에 따라 틀리다는 것입니다. while문은 조건반복일 경우, for문은 count반복일 경우에 쓰인다는 것을 기억해야 합니다.


6-5. 건너뜀 구조

건너뜀 구조는 현재 반복이나 조건 루틴에서 탈출하거나 특정부분을 수행하지 않고 다시 돌아가는 구조 또는 특정 블록으로 점프하는 구조를 말합니다.

  그림 6-7 break 구조                그림 6-8 continue 구조



건너뜀 구조를 설명하면, 어떤 프로그램이 수행되다가 임의의 조건에 의해서 loop를 빠져 나가거나 건너띠는 경우를 말하는 것입니다 그림 6-7)를 보면 처음 start 해서, comp 비교해서 yes냐 no를 경정하는데 no를 선택하면 바로 command3 으로 갈 것이고 yes하면 command1 으로 갈 것입니다. 그리고 순차적으로 command2로 가야하는데 중간에 if가 있지요? 이 if문에 조건이 합당하면 command2 로 가지안고 루프를 빠져나가서 command3으로 가게 됩니다. 결국 보면, comp해서 비교를 해 가지고 이 조건에 의해서 빠져나가는 것이 아니라, 루프 중간에 조건을 하나 달아서 루프를  빠져나가는 경우가 있습니다. 다시 말하면 반복을 계속 하는데 위의 조건에는 상관없이 빠져나갈 경우를 건너뜀 구조라 합니다. 이렇게 건너뛸 때 이 때의 문장이 break문입니다.

그림 6-8)을 보면 start해서 comp에서 비교합니다. 여기서 yes하면 command1을 수행하고, 다음으로 들어가는데, 여기서 조건이 데이터와 일치하는 안는다면 continue에서 ’그 조건에 의해서 수행하지마’라는 명령과 함께 command2를 수행하지 안고 comp로 바로 이동되게 됩니다.

6-6.break 와 continue

break문과 continue문은 블럭에서 탈출이나 또는 특정 부분을 수행하지 않고 처음으로 돌아가는 형태입니다. 6-5에서 문의 구조를 순서도로 설명하였습니다. 그림 6-9는 break와 continue를 사용한 순서도입니다.

그림 6-9) break 문과 continue 문이 사용되는 순서도


그림 6-9)을 프로그램밍 하면 다음과 같은 소스를 만들 수 있습니다


#include <stdio.h>

void main()

{

        int a, b, total, c;

        printf("종료 값을 입력하시오\n");

        scanf("%d", a);

        printf("처리하지 안는 값을 입력하시오\n");

        scanf("%d", b);

        total = 0;

        while(1)

        {

                scanf("%d", c);

                if (c == a)

                        break;

                if (c == b)

                        continue;

                total = total + c;

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

        }

}


break문은 중괄호 바깥으로 빠져나간다 라는 것입니다. 다시 말하면 어떤 조건에 의해서 프로그램이 진행되는 도중에 그 조건이 지정하는 루프가 다 끝나지 안았어도 중간에 루프를 빠져가는 명령문입니다 루프도중에 조건을 만나면 조건에 맞는지 맞지 안는지를 보고 맞으면  break만나게 되고 break문을 만나면 수행중인 루프 바깥으로 빠져나가게 되는 것입니다. 만약 틀리다면 다음 코드로 넘어가게 되는데 코드를 보면 다시 조건문이 나오게 됩니다 만약 이 조건에 합당하게 되면 프로그램은 데이터는 continue를 만나게 되고 total = total + 1 연산을 수행하게 됩니다 그런 다음 결과를 화면에 출력하게 됩니다 그런 다음 어떻게 될까요 앞에서 while 문을 설명할 때 while 문안에 조건이 1 이 되면 항상 참이 되고 그럼 무한 루프를 돌게 된다고 말씀드렸지요 여기 코들를 보면 while문의 조건이 1 로 되여 있습니다 그래서 프로그램은 무한 루프를 돌게 됩니다 그럼 이 루프를 바깥으로 나가려면 어떻게 해야 할까요? a 값과 c 값이 같아지면 됩니다. 이래서 break문과 continue문이 바로 jumping 건너뛰게 하는 본래 대표적인 문장들입니다.  순서도가 굉장히 복잡할 때는 굉장히 복잡하다만, 이렇게 간단한 것부터 하나 하나씩 그림을 그려가 보면 그렇게 어렸지 안다라는 것을 아실 수있으실 것입니다.

6-8 goto 문

C언어 문장중에서 필자가 가장 설명하기 싫고 그리고 사용하지 않았으면 하는 문장이 goto문입니다.

이문은 간단하게 예제를 보여주면서 설명하겠습니다. 다음 예제는 goto문을 사용하여 무한반복을 탈출한 예제입니다.

#include <stdio.h>


void main()

{

        int d;

        while(1)

        {

                printf("입력할 수:");

                scanf("%d",&d);

                if(d<0)

                        goto end;

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

        }

end: printf("반복 종료\n");

}

while 무한 반복문 안에 goto 라는 문장과 함께 end이라는 인수가 있습니다. 이문장은 end 문장이 있는 곳으로 점프 하라는 내용입니다. if(d<0)의 조건에 합당하면 end 라벨이 붙어있는 곳으로 점프하라는 의미입니다. while반복문 블록 다음에 end: 라고 설정된 라인이 있습니다. 바로 이곳으로 점프하라는 의미입니다. goto 문을 사용하게 되면 프로그램의 흐름을 알기가 매우 어렵습니다. goto 문은 함수의 특정위치로 갑자기 분기하는 형태이기 때문에 C언어의 구조 흐름을 분석하는데 난해한 단점이 있으며 과거 16비트 시절에는 세그먼트 내에서 점프가 가능한 단점도 포함합니다. 실제로 goto문 대신에 break; 문이나 continue 문을 이용해도 건너뜀이 가능합니다.

가끔 반복의 4겹으로 설정된 경우 이런 문을 사용할때가 있고 다른 소스를 분석할 때 때때로 설정되어 있기 때문에 본항목에서 설명합니다.


6-8.프로그래밍 구조의 최적화

순서도를 그리다 보면 느낌이 오는데, 어떤 느낌이 나오는가 하면, 순서도를 보는 순간 이것은 최적화 된 것이다 혹은 최적화 된 것이 아니다 라는 느낌이 옵니다. 잘못 만들어진 순서도를 보겠습니다

그림 6-10)잘못된 순서도


그림 6-10)은 잘못 만들어진 순서도의 예입니다 이 프로그램은 switch문에 의해서 값이 case1, case2, case3일 경우 전부 다 command1, command4를 10번씩 반복해서 무슨 값이 바뀌어서 계속해서 10번씩 반복해 돌아가고, 그런 다음 command4로 넘어가는 프로그램입니다. 이것을 보면 switch문에 의해서 10개의 반복이 그림 6-10)의 왼편에서도, 중앙에서도 오른편에도 똑같이 일어난 다는 것을 보실 수 있습니다. 무엇인가 좀 최적화 되어 있지않다 라는 생각이 드실 것입니다. 이 순서도를 최적화해서 프로그램을 다시 만들어 보겠습니다.



그림 6-11 최적화 된 순서도

그림 6-11)은 그림 6-10)에서 보여주었던 프로그램을 최적화 해서 다시 순서도를 그린 것입니다. 프로그램을 실행 시켰을때 그림 6-10)과 그림 6-11)은 같은 결과 값을 보여주게 됩니다. 6-10은 switch 문에 3개의 다중 분기가 되고 다중 분기가 되었을때 for 반복이 들어간 구조 입니다. 여기에서 for 반복의 내용이 같습니다. 결국 같은 반복 모듈이 3개나 따로 연결되어 있는 형태입니다. 6-11은 for 반복 안에 switch문이 있습니다. 반복은 한개요 한개의 한에 단 3개의 Command 블록의 분기가 발생됩니다. Command 블록의 수를 세어 보아도 6-11이 6-10보다 훨씬 최적화 되어 있다는 것을 느낄것입니다. 순서도를 만들어 보게 되면 프로그래밍의 최적화를 한눈에 볼수가 있습니다. 그러나 단순히 언어 프로그래밍만 하게되면 6-11이나 6-10의 차이점을 자세히 보지 않는한 알아내기가 힘들게 됩니다. 순서도와 연동한 프로그램은 구조적 모순을 많이 가지고 있지 않다는 예를 보여주는 것입니다.

6-9 결론

프로그래밍과 설계는 매우 밀접한 관계를 가지고 있습니다. 실제 설계에서 전체 프로그램은 객체 지향방식으로 설계합니다. 그러나 하부로 갈때는 모듈로 세분화 되는데 이때는 순서도를 주로 이용합니다. 많은 분들은 이부분에 대해서 매우 쉽게 생각하는 경향이 있습니다. 필자는 현재 15년동안 프로그래밍을 해왔습니다. 제가 성공한 프로그램도 있지만 실패한 프로그램도 많습니다. 실패한 프로그램의 가장 큰 원인은 설계가 없었다는 것입니다. 지금도 수많은 프로그래머들이 설계없이 프로그램을 작성하고 있습니다. 시간과 자금에 쫒겨서 말입니다. “어쩔수 없이 설계없이 코딩한다” 라는 말을 이해합니다만 그래도 “설계가 중요하다” 라는 것을 꼭 기억하시기 바랍니다. 후에 시간과 자금이 여유가 있을때 멋진 프로그램을 제작하기 위해서 입니다.

 

 

 

 

 

 

 

 

 

 

<자료출저 : 삼육대학교 이상엽 교수>

댓글 없음:

댓글 쓰기