6-4-라.Turboc.h

함수의 정의와 원형의 필요성, 그리고 매크로 함수까지 알아보았으니 지금까지 미루어 왔던 Turboc.h 파일을 분석해 보자. 실습 초반부터 지금까지 이유도 설명하지 않고 계속 이 파일을 사용해 왔는데 이제 드디어 이 헤더 파일을 분석해 볼 수 있게 되었다. 전체 소스는 다음과 같다.

 

: Turboc.h

// 혼자 연구하는 C/C++의 도우미 헤더 파일

// 비주얼 C++ 환경에서 터보 C 스타일의 함수를 정의한다.

#ifndef TURBOC_HEADER

#define TURBOC_HEADER

 

#include <stdio.h>

#include <stdlib.h>

#include <conio.h>

#include <time.h>

#include <windows.h>

 

typedef enum { NOCURSOR, SOLIDCURSOR, NORMALCURSOR } CURSOR_TYPE;

void clrscr();

void gotoxy(int x, int y);

int wherex();

int wherey();

void setcursortype(CURSOR_TYPE c);

 

#define delay(n) Sleep(n)                              // n/1000초만큼 시간 지연

#define randomize() srand((unsigned)time(NULL))         // 난수 발생기 초기화

#define random(n) (rand() % (n))                        //0~n까지의 난수 발생

 

// 이 매크로가 정의되어 있으면 함수의 원형만 선언하고 정의는 하지 않는다.

#ifndef TURBOC_PROTOTYPE_ONLY

 

// 화면을 모두 지운다.

void clrscr()

{

     system("cls");

}

 

// 커서를 x,y좌표로 이동시킨다.

void gotoxy(int x, int y)

{

     COORD Cur;

     Cur.X=x;

     Cur.Y=y;

     SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),Cur);

}

 

// 커서의 x 좌표를 조사한다.

int wherex()

{

     CONSOLE_SCREEN_BUFFER_INFO BufInfo;

 

     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&BufInfo);

     return BufInfo.dwCursorPosition.X;

}

 

// 커서의 y좌표를 조사한다.

int wherey()

{

     CONSOLE_SCREEN_BUFFER_INFO BufInfo;

 

     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&BufInfo);

     return BufInfo.dwCursorPosition.Y;

}

 

// 커서를 숨기거나 다시 표시한다.

void setcursortype(CURSOR_TYPE c)

{

     CONSOLE_CURSOR_INFO CurInfo;

 

     switch (c) {

     case NOCURSOR:

          CurInfo.dwSize=1;

          CurInfo.bVisible=FALSE;

          break;

     case SOLIDCURSOR:

          CurInfo.dwSize=100;

          CurInfo.bVisible=TRUE;

          break;

     case NORMALCURSOR:

          CurInfo.dwSize=20;

          CurInfo.bVisible=TRUE;

          break;

     }

     SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&CurInfo);

}

 

#endif // TURBOC_PROTOTYPE_ONLY

#endif // TURBOC_HEADER

 

선두에서 stdio.h, conio.h 등 C언어의 표준 헤더 파일을 포함시키고 있는데 Turboc.h가 이 파일들을 포함하고 있기 때문에 예제에서는 이 헤더만 포함하면 표준 함수들을 그냥 사용할 수 있었던 것이다. #include는 중첩을 허용하며 헤더 파일이 포함한 파일까지 주 파일에 모두 같이 포함된다.

표준 헤더 파일 include문 다음에 함수 원형이 있고 3개의 매크로 함수와 5개의 일반 함수를 정의하고 있다. 먼저 매크로 함수부터 분석해 보자. delay는 지정한 시간만큼 실행을 지연시키는데 Sleep이라는 Win32 API 함수 호출문으로 치환된다. Sleep은 delay와 이름만 다르고 동작하는 방식은 유사하되 API 함수이므로 windows.h 헤더 파일을 포함해야 한다.

randomize 함수는 현재 시간값으로부터 난수 발생기를 초기화하며 random 함수는 %연산자의 특성을 사용하여 인수로 주어진 n 이하의 난수를 생성한다. 이 두 매크로는 터보 C 컴파일러에도 동일한 형태로 매크로 정의되어 있는데 본래 함수인 srand나 rand보다는 초보자가 더 직관적으로 이해할 수 있고 사용하기도 편리하다. 비주얼 C++의 헤더 파일은 이 두 함수에 대한 매크로 정의를 하지 않기 때문에 터보 C의 매크로 정의문을 이 헤더에 따로 작성해 두었다.

화면을 지우는 clrscr은 cls 시스템 명령을 호출하도록 되어 있고 커서의 위치를 조사, 변경하는 함수, 커서의 모양을 변경하는 함수들은 대응되는 Win32 콘솔 API 함수를 호출한다. 이 함수들의 본체에 사용된 코드는 Win32 콘솔 API를 공부해 보면 아주 쉽게 분석되는 것들이므로 차후에 콘솔 API를 배울 때 직접 분석해 보기 바란다. 이 외에 Turboc.h에는 안전을 위해 몇 가지 조건부 컴파일 지시자가 포함되어 있다.

Turboc.h에 정의된 delay, gotoxy 등의 함수들은 터보 C 컴파일러에는 실존하는 함수들이다. 그래서 이 함수들은 printf나 scanf 함수처럼 C언어를 처음 배우는 사람들에게 아주 빈번히 애용되었으며 조건문이나 루프의 개념, 연산자의 동작 상태를 연구해 보기 위한 좋은 도구가 되었다. gotoxy 함수는 루프의 실행 과정을 화면에 가시적으로 보여주며 delay 함수는 이 결과를 좀 더 분명히 목격할 수 있도록 도와 준다.

C 언어를 처음 배우는 사람에게 연산자니 조건문이니 루프니 하는 개념은 선뜻 이해가 되지 않는 생소한 개념이다. 이런 어려운 개념에 대해 계속 이론만 나열하면 너무 지루하기 때문에 화면에 가시적인 결과로 보여주고 예제를 직접 만들어 보게 하는 방법이 전통적으로 많이 사용되었으며 이 책도 이런 전통을 따르기 위해 상기 함수들을 부지런히 애용해 왔다.

하지만 이 책에서 선정한 컴파일러인 비주얼 C++에서는 안타깝게도 이런 함수들이 제공되지 않는다. 왜냐하면 비주얼 C++은 교육용 컴파일러가 아닌 실무용 컴파일러인데다가 텍스트 기반 프로그램 제작용 툴이 아니라 화려한 그래픽 기반의(GUI) 고성능 응용 프로그램 제작을 위한 툴이기 때문이다. 물론 비주얼 C++로 처음부터 GUI예제를 작성한다면 윈도우나 대화상자를 만들어서 멋드러진 그래픽 출력 예제를 만들 수도 있다. 그러나 이는 C언어를 처음 배우는 사람들에게는 너무 너무 복잡해서 적합하지 않다. C 문법도 복잡한데 윈도우니, DC니, 메시지니 하는 개념은 도무지 초보자가 다룰 대상이 되지 못하는 것이다.

C 문법을 공부할 때는 운영 환경이 가급적이면 단순해야 하며 이런 목적으로는 도스나 콘솔창이 제격이다. 그렇다고 해서 20년전에 발표된 터보 C를 지금에 와서 교육용 목적으로 사용하는 것은 또한 무리다. 16비트라 지금 환경과는 잘 맞지 않은 면이 너무 많고 차후에 실무 개발로 넘어갈 때 또 한 차례의 진통이 예상되기 때문이다.

이 책의 저자는 이런 저런 이유로 적합한 교육용 컴파일러 선정을 위해 장기간에 걸친 심사숙고 끝에 32비트 컴파일러를 채택했다. 처음에 조금 어렵더라도 실무에 바로 쓸 수 있는 툴로 배우는 것이 더 현명하다고 판단했기 때문이다. 대신 초보자를 이해시키기 위한 예제 제작을 위해 터보 C의 환경을 그대로 모방할 수 있는 헤더 파일을 제공하는 방안을 선택함으로써 실습의 효율과 빠른 이해를 달성하고자 했다. 그 결과가 바로 Turboc.h라는 헤더 파일이다.

물론 이해하지도 못하는 헤더 파일을 무조건 포함하라고 강요하는 것도 초보자에게 부담스러운 것이 사실이다. 여기까지 공부를 해 온 여러분들은 내가 왜 이런 고민을 해야 했는지 어느 정도는 이해해 줄 것으로 믿으며 이제 더 이상 Turboc.h를 예제 프로젝트에 포함시키는 것에 대해 의문점을 가지지 않을 것이다. 하지만 다음 사항은 잘 알아 두도록 하자.

 

Turboc.h는 어디까지나 교육의 편의를 위해 작성된 헤더 파일이지 실제로 컴파일러와 함께 제공되는 표준 헤더 파일이 아니다. 따라서 실무를 할 때는 이 헤더 파일이 없으므로 직접 작성하든가 아니면 복사해 가야 한다. 그래픽 환경에서라도 random같은 매크로는 꽤 편리하므로 훔쳐갈만 하다. 실무에서 #include <Turboc.h> 같은 문장을 쓰게 되면 아마도 상사에게 왜 그런 이상한 코드를 구해 쓰느냐고 핀잔을 듣게 될 것이다.

이 헤더 파일은 자신이 정의하고 있는 함수들을 위해 표준 헤더 파일과 운영체제의 헤더 파일까지 인클루드하고 있다. 그래서 Turboc.h가 포함한 헤더 파일에 선언된 함수들은 별도의 선언없이 곧바로 사용할 수 있는데 원래부터 그렇다고 생각해서는 안된다. printf 함수를 사용하고 싶을 때 필요한 헤더 파일은 Turboc.h가 아니라 stdio.h라는 것을 알아야 하며 getch를 쓰고 싶으면 반드시 conio.h를 인클루드해야 함을 숙지하도록 하자.

이 책에서 만드는 예제는 대부분 단일 모듈 프로젝트이기 때문에 Turboc.h는 단일 모듈을 가정하고 작성되어 있다. 이 헤더만 포함하면 gotoxy, clrscr 함수를 바로 쓸 수 있도록 되어 있는데 헤더 파일에는 선언만 들어가야지 함수의 본체를 작성하는 것은 바람직하지 않다. 원칙대로 이 함수들을 재사용하려면 헤더 파일과 구현 파일을 따로 만들고 구현 파일을 프로젝트에 포함시키는 것이 맞지만 이렇게 하자면 실습이 너무 번거로와지기 때문에 그렇게 하지 못한 것이다. 만약 두 개 이상의 모듈에서 이 헤더를 포함한다면 두 번째 모듈에서는 인클루드 전에 TURBOC_PROTOTYPE_ONLY 매크로를 반드시 정의해야 한다.

Turboc.h라는 헤더 파일의 이름은 과거의 도스용 터보 C 컴파일러 환경을 흉내내는 함수를 정의한다는 뜻을 가지고 있다. 전통적으로 많이 작성되어 온 효율적인 예제 제작을 위해서는 역시 도스 환경이 가장 적합하기 때문이다. 그러나 역설적이게도 이 헤더 파일을 포함한 예제는 진짜 터보 C에서는 컴파일되지 않는데 윈도우즈의 콘솔 제어를 위해 windows.h라는 헤더 파일을 포함하고 있기 때문이다. 터보 C류의 도스용 컴파일러를 사용하려면 이 헤더 파일을 아예 빼고 컴파일해야 한다.

 

실무용 컴파일러를 교육용으로 억지로 둔갑시키려고 하다 보니 이런 어색한 방법을 쓰게 되었는데 막상 사용해 보니 나름대로 실습의 편의성을 높이는 효과는 있는 것 같다. 그래서 앞으로도 특별한 이유가 없는 한 계속 애용할 생각이므로 저자의 깊은 고민을 헤아려 주기 바란다.