IT/C and C++

조건부 컴파일러 - 선행처리기(Preprocessor)

KSI 2005. 7. 11. 03:27
선행처리기(Preprocessor)

본장에서는 컴파일 하기전에 특정 문장을 미리 정의하는 선행처리기에 대해서 공부합니다. 선행처리기는 C언어에 포함되어 있는 것은 아닙니다. 그러나 C언어에서 프로그래밍을 할 때 좀더 간결하고 편리하게 하기위한 보조 역할을 하는 형태입니다.컴파일러가 프로그램을 컴파일 하기전에 이 선행처리문을 C언어 형태로 바꾼후에 이것을 컴파일 합니다. 그러나 선행처리기를 잘 활용함으로써 프로그램을 강력하게 또한 디버깅 및 프로그램의 버전업을 쉽게 할 수가 있습니다. 본장에서는 #define,#if,#ifdef 등등의 선행처리기의 프로그래밍 방법을 설명합니다.

매크로 정의 #define

프로그램을 제작할 때 자주 사용되는 수들이 있을수 있습니다. 예를 들어서 수학 계산을 자주 하는 프로그램에서는 원주율

를 자주 사용합니다.

pi

는 보통 3.14로 계산을 하지요 이렇게 자주 사용하는 수를 정의하는 문이 바로 #define입니다.

#define PI 3.14

위의 문장은 3.14를 PI로 정의한다는 내용입니다. 위와 같이 정의한후에 3.14대신에 PI를 사용하시면 됩니다. 예를 들어 원의 면적을 구할 경우 다음과 같이 사용하면 됩니다.

float area,r;

r=2.5f;

area=r*r*PI;

위의 모듈이 실행 하기 이전에 #define문이 설정되어서 PI를 정의해 두어야 합니다.

#define는 상수를 정의할 때 또는 매크로를 정의할 때 많이 사용합니다.

자주 출력하는 문장 또한 define로 정의할수 있습니다.

#define INSA(hdc) Textout(hdc,0,0,"안녕하세요",10)

위와 같이 정의해 놓으면 INSA를 쓰면 화면 상단에 안녕하세요가 출력이 됩니다.

INSA;//화면 상단에 "안녕하세요 출력"

아주 간단한 함수들은 define의 매크로를 이용하여 출력을 할수 있습니다.

#define cylen(a) 2*PI*a //원의 길이를 구하는 식

위의 문장은 원의 길이를 구하는 식 cylen함수를 아주 간단하게 define로 설정한것입니다. 위의 식을 호출할때는 다음과 같이 합니다.

float len,r;

r=2.5f;

len=cylen(r);

매크로 정의 #define 예제 Exam27

Exam27은 Exam26처럼 factory.c factory.h라는 파일을 만들고 그안에 원의 길이 원의 면적을 구하는 식과 팩토리얼을 구하는 식 및 원주율을 정의해두고 이것을 출력하는 예제입니다.

(프로그램 소스)

//파일:factory.h

#define PI 3.14F //원주율 정의

#define INSA(hdc) TextOut(hdc,0,0,"안녕하세요",10)//인사 출력 정의

#define cylen(a) 2*PI*a //원의 길이 정의

int factory(int n); //팩토리얼 함수

float cyclearea(float r); //원의 면적 함수

////////////////////////////////////////////////////////////////////////////////

//파일: factory.c

#include "factory.h"

int factory(int n)

{

if(n <=1)

return 1;

else

return(n*factory(n-1));

}

float cyclearea(float r)//원면적 구하는 함수

{

float gab;

gab=r*r*PI;

return gab;

}

////////////////////////////////////////////////////////////////////////////////

//파일 Exam27.c

//define예제

#include <windows.h>

#include <stdio.h>

#include "factory.h"

void main(HDC hdc)

{

char temp[80];

int n=4;

float r;

INSA(hdc);

sprintf(temp,"원주율 PI=%f",PI);

TextOut(hdc,0,20,temp,strlen(temp));

r=2.4f;

sprintf(temp,"반지름이 %f인 원의 면적은=%f",r,cyclearea(r));

TextOut(hdc,0,40,temp,strlen(temp));

sprintf(temp,"반지름이 %f인 원의길이는 =%f",r,cylen(r));

TextOut(hdc,0,60,temp,strlen(temp));

}

(프로그램 소스끝)

사용자 삽입 이미지

(그림 1)Exam27 출력 결과 (cap509.bmp)

매크로 정의 취소 #undef

#undef 라는 현재 정의되어 잇는 매크로를 취소할 때 사용합니다. 예를 들어 현재 PI라는 것이 다음과 같이 정의되어 있을 경우

#define PI 3.14

위의 정의된 내용을 사용하지 않을 경우 이때

#undef PI

해주시면 PI의 매크로 정의를 취소할수 있습니다. 매크로정의 취소가 필요한 경우는 현재 정의된 것이 재설정이 필요할경우가 많습니다. 위의 예에서 어떤 경우에는 PI를 3.14로 설정하였는데 또 어떤경웨는 PI를 3.142598 로 정의하여 쓰고 싶을 경우가 있을것입니다. 이럴 경우 #undef와 #define를 혼합하여 사용합니다.

#define PI 3.14 // 처음정의

: //프로그램에서 3.14로 사용함

#undef PI //PI 정의를 취소한다.

#define PI 3.142598 //PI를 재정의

조건부 컴파일 #if,#else,#elif,#endif

조건부 컴파일 정의란 컴파일 이전에 어떤 조건에 따라 정의 내용을 변경하는 선행처리기입니다. 보통 식은 다음과 같습니다.

#if 식1

: //식1 이 합당할 때 선행되는 내용

#elif 식2

: //식1 이 합당하지 않고 식2가 합당할 때 선행되는 내용

#else

: //위의 내용이 모두 맞지 않을 경우 선행되는 내용

#endif //조건부 정의 종료

조건부 컴파일은 보통 한 개의 프로그램을 제작하였을 경우 프로그램이 구동하는 환경이 여러개일 경우 이것에 해당하는 정의를 바꾸어 해당 환경에 맞게 컴파일 할 때 사용합니다. 예를 들어서 윈도95와 윈도NT두개에서 공용적으로 구동하는 프로그램이 있다고 가정을 합시다. 그리고 윈도 NT가 설치되어 있는 컴퓨터는 메모리가 매우크고 윈도95가 설치되어 있는 컴퓨터는 메모리가 작다고 가정을 할 때 해당 컴퓨터에 맞게 메모리를 설정할 필요가 있습니다. 이럴 경우 조건부 컴파일을 자주 이용합니다.

필자가 프로그래밍을 할 경우 보통 config.h라는 하나의 헤더를 나두고 그곳에 다음과 같이 OSSTYLE를 설정합니다.

(config.h 파일)

#define OSSTYLE 1

//윈도95일 경우 1 윈도 NT일 경우 2 그밖에것은 3

다음 정의 되는 헤더에 다음과 같이 기록합니다.

#include "config.h"

#include "config.h"

#if OSSTYLE == 1 //윈도95일경우

#define BLOCKSIZE 6400

#elif OSSTYLE == 2

#define BLOCKSIZE 64000//윈도 NT일경우

#else

#error Can't find os style

#endif

즉 OS에 따라서 BLOCKSIZE 가 변하게 됩니다. 후에 이 BLOCKSIZE 만큼 메모리를 사용하도록 프로그래밍 하면 컴퓨터 환경에 맞게 컴파일이 되기 때문입니다.

한 개의 프로그램을 작성하고 난후에 컴퓨터 및 OS의 환경에 맞게 프로그램을 변경시킬 경우 위와 같이 하였을경우에는 config.h안에 있는 OSSTYLE의 정의 값을 변경만하고 재컴파일 하면 되니까요?

위의 예에서 #error이라는 새로운 형이 있는데 이것은 에러 값을 리턴하고 컴파일을 중단하라는 선행처리기입니다. 위의 예제에서는 OSSTYLE가 1이거나 2일경우에는 컴파일을 하지만 다른 값에서는 #error에 기록한 글자가 오류로 나타나면서 컴파일이 중단됩니다.

조건부 컴파일은 여러 OS에서 또는 여러 컴파일러에서 호환적으로 프로그램이 컴파일 되도록 하는 방법으로 많이 이용됩니다. 예를 들어 윈도에서는 LPSTR이라는 형이 있으나 유닉스나 리눅스에서는 지원되지 않는 형입니다. 이럴 경우 이형을

정의하여 윈도에서 만든 프로그램이 유닉스에서도 컴파일 될 수 있도록 할수 있습니다.

#define OSSTYLE 3

#if OSSTYLE ==3 //유닉스이면

#define LPSTR char*

:


조건부 컴파일 예제 Exam28

Exam28은 OSSTYLE를 정의하고 이 정의 값에 의해 BLOCKSIZE가 변화는 예제입니다.

(프로그램 소스)

////////////////////////////////////////////////////////////

//파일명:config.h

#define OSSTYLE 1

////////////////////////////////////////////////////////////////

//파일명 test.h

#include "config.h"

#if OSSTYLE == 1

#define BLOCKSIZE 6400

#elif OSSTYLE == 2

#define BLOCKSIZE 64000

#else

#error Can't find os style

#endif

////////////////////////////////////////////////////////////////////////////////

//파일 Exam27.c

//define예제

#include <windows.h>

#include <stdio.h>

#include "test.h"

void main(HDC hdc)

{

char temp[80];

sprintf(temp,"OS 형태는 %d 입니다.",OSSTYLE);

TextOut(hdc,0,0,temp,strlen(temp));

sprintf(temp,"BLOCKSIZE 는 %d 입니다.",BLOCKSIZE);

TextOut(hdc,0,20,temp,strlen(temp));

}

(프로그램 소스끝)

그림 2는 Exam29의 출력 결과입니다. config.h의 값을 변경시키면서 결과를 알아보시기 바랍니다.

 

 

사용자 삽입 이미지

(그림 2)Exam28 출력 결과 (cap602.bmp)

 

메크로 정의확인 #if defined,#if !defined, #ifdef,#ifndef

매크로 정의 확인 선행처리기는 현재 어떤 정의의 유무를 물어보고 해당결론에 따라서 선행 처리문이 실행됩니다.

#if defined 식별자

: 식별자가 정의 되었을 때 실행

#if !defined 식별자

: 식별자가 정의 되어 있지 않을 때 실행

#ifdef 식별자

: 식별자가 정의 되었을 때 실행

#ifndef 식별자

: 식별자가 정의 되어 있지 않을 때 실행

#if defined와 #ifdef와는 기능이 같고 #if !defined와 #ifndef의 기능이 같습니다.

차이점은 #if defined와 #if !defined는 #elif,#else,#endif와 함께 사용할수 있으나 #ifdef와 #ifndef는 그렇게 할수 없다는 것이 차이입니다.

Visual C++ 컴파일어 헤더에는 TRUE와 FALSE가 정의되어 있습니다. 그러나 이런값들이 정의 되어 있지 않는 윈도우 컴파일러가 만일 있다면 프로그램을 제작하여 다른 컴파일러로 컴파일 할 경우 에러가 나올것입니다. 호환성을 갖추기 위해서 정의 되어 있지 않을 경우 정의를 해줄 때 매크로 정의 확인 선행 처리문을 사용합니다.

#ifndef TRUE

#define TRUE 1

#ifndef FALSE

#define FALSE 0

위와 같이 설정을 하면 TRUE와 FALSE라는 값이 정의 되어 있지않는 컴파일러에서만 TRUE와 FALSE를 정의하게 됨으로써 컴파일러를 호환적으로 사용할 수가 있는 것이지요.

#programa

#programa 는 프로그래머가 프로그램 중에서 컴파일러에게 어떤 내용을 지시할 때 사용하는 선행처리문입니다. 도스시절에는 자주 사용하였던 선행 처리문이었으나 현재는 자주 사용하지 않습니다. 주로 프로그램내부에 어셈블리어를 사용하고자 할 때 사용합니다. 어셈블리어를 사용할때는 다음과 같이 사용합니다.

#programa inline

#programa와 함께 사용하는 식별자는 컴파일러에 따라 다릅니다. 보통 Visual C++에서는 message,pack등의 지시자가 있으나 거의 사용은 하지 않습니다.

시스템에 정의된 매크로

C에서는 미리 정의되어 있는 매크로 들이 있습니다. 이것들은 보통 컴파일의 시간 및 날짜와 현재 컴파일되는 라인 그리고 파일명 등을 알려줍니다. 시스템에 정의된 매크로 선행처리문은 다음과 같습니다.

__FILE__ 현재 컴파일되는 파일명을 문자열로 리턴합니다.

__TIME__ 컴파일 되는 현재 시각을 문자열로 리턴합니다.

__DATE__ 컴파일 되는 현재 날짜를 문자열로 리턴합니다.

__LINE__ 컴파일 되는 현재 파일의 라인을 정수로 리턴합니다.

Exam29는 시스템 정의 매크로를 사용한 예제입니다.

(프로그램 소스)

////////////////////////////////////////////////////////////////////////////////

//파일 Exam28.c

//시스템 정의 매크로

#include <windows.h>

#include <stdio.h>

void main(HDC hdc)

{

char temp[80];

sprintf(temp,"현재 소스 라인: %d",__LINE__);

TextOut(hdc,0,0,temp,strlen(temp));

sprintf(temp,"컴파일 시간: %s",__TIME__);

TextOut(hdc,0,20,temp,strlen(temp));

sprintf(temp,"컴파일 날짜: %s",__DATE__);

TextOut(hdc,0,40,temp,strlen(temp));

sprintf(temp,"파일명: %s",__FILE__);

TextOut(hdc,0,60,temp,strlen(temp));

}

(프로그램 소스끝)

그림 3은 Exam29 출력 결과입니다.

 

사용자 삽입 이미지

그림 3)Exam29 출력 결과 (cap603.bmp)

pi

«   2024/12   »
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

방문자수Total

  • Today :
  • Yesterday :