본문 바로가기

C-Language/Preprocessors

Preprocessors in C language

C 언어 전처리기 ( Preprocessors )



C 언어에서 전처리기는 #으로 시작되는 키워드를 말한다. 예를 들면, #include, #define 등과 같은 것인데 쉽게 설명하자면 코드를 컴파일하기 전에 코드 중에서 전처리 키워드가 사용된 부분을 미리 지정한 텍스트로 대치하는 기능을 가진다.

전처리 명령어는 함수 안이나 밖에서도 사용할 수 있다


주요 전처리 명령어

전처리 명령 (Directives) 

  기능

#define    

   전처리 매크로가 사용된 부분을 지정된 코드로 대치한다

 #include    

   다른 파일에 저장된 헤더파일을 현재 파일에 포함한다 

 #undef    

   전처리 매크로를 해제한다 

 #ifdef    

   전처리 매크로가 선언되어 있으면 true 를 리턴한다 

 #ifndef    

   전처리 매크로가 선언되지 않았으면 true 를 리턴한다 

 #if    

   조건이 true 인지 검사한다 

 #else    

    #if 의 조건이 false 이면 실행된다

 #elif    

   #if, #elif, #else는 조건을 검사할 때 함께 사용된다 

 #endif    

   #if 의 종료 

 #error    

    stderr 를 이용하여 오류 메시지를 출력한다

 #pragma    

   컴파일러에게 특별 명령을 전달한다 


전처리 명령의 예

#define PI 3.141592

#define 전처리 명령은 프로그램의 가독성( readability )을 위해 프로그램 코드의 특정 부분에 매크로를 사용하고 컴파일 전에 실제 코드로 대치할 때 사용된다

위의 경우는 프로그램 코드 중에 PI 라는 전처리 매크로를 사용한 곳이 있으면 3.121592로 대치한다


#include <stdio.h>

#include "myheader.h"

현재 코드에 지정된 헤더파일을 추가한다

라이브러리에 컴파일되어 있는 함수나 다른 개발자가 작성한 함수를 사용하기 위해서는 현재의 코드에 해당 함수를 선언할 필요가 있는데, 이 때 많은 함수가 사용된다면 헤더파일에 함수를 선언하고 그 헤더파일을 현재의 코드에 포함하면 간편하다

<stdio.h>는 표준 시스템 헤더파일을 포함하는 경우이고 "myheader.h" 는 현재의 코드와 같은 곳에 헤더파일이 있는 경우이다


#undef RATIO 1.2

#define RATIO 2.3

이미 선언된 전처리 매크로 RATIO를 해제하고 새로 선언할 때 매크로를 선언한다. 이로 인해서 RATIO 라는 전처리 매크로가 사용된 곳은 컴파일될 때 2.3으로 대치된다


#ifndef MAIN_HEADER

#define MAIN_HEADER

#include "mainheader.h"

#endif

MAIN_HEADER 라는 매크로가 이미 정의되어 있지 않으면 MAIN_HEADER 라는 매크로를 정의하고 "mainheader.h" 파일을 포함한다

다수개의 코드파일로 구성된 프로그램에서는 코드파일 마다 공통 헤더파일을 포함하는 경우가 있는데, 소스코드를 컴파일하여 링커에 의해서 컴파일된 코드가 합쳐질 때 동일한 헤더파일이 중복되어 포함되는 문제가 있으므로 헤더파일을 한번만 포함하도록 위와 같은 전처리 명령을 사용할 필요가 있다



#ifdef DEBUG

   printf("%s \n", message);

#endif

DEBUG 매크로가 선언된 경우에만 화면에 특정 변수의 값을 출력하고자 할 때 사용한다

주로 개발자가 디버깅 목적으로 개발시에만 특정 코드 부분을 실행하기 위해서 사용되며 개발이 완료되면 DEBUG 라는 매크로를 제거하여 해당 부분이 실행되지 않도록 할 수 있다



미리 내장된 전처리 매크로 (Predefined Macros)

ANSI C 언어는 개발의 편의성을 제공하기 위해 몇개의 전처리 매크로를 내장하고 있다

__DATE__   :  현재 날짜

__TIME__   :  현재 시간

__FILE__    :  현재 소스파일의 파일명

__LINE__   :  현재 코드의 행번호를 10진수로 표현

__STDC__  :  현재의 컴파일러가 ANSI C 컴파일로 설정되어 있는 경우에 1 로 설정됨 ( VC 에는 없음 )



전처리 연산자, 매크로 연속 연산자( '\' ), 역슬래시

일반적으로 매크로는 실제 컴파일 시에 대치될 내용을 표현하며 한 행으로 나타낼 수 있다. 그러나 매크로가 한 행을 넘어서 정의되는 경우에는 특별한 연산자(매크로 연속 연산자, '\' )를 사용하여 매크로가 한 행을 넘어 그 다음 행도 매크로가 선언되고 있다는 것을 표시해야 한다

참고 : 아래의 코드를 보기 전에 printf("A" "B") 형태의 문장도 문법적으로 가능하다는 것을 이해하고 있어야 한다


전처리 연산자, 문자열화 연산자 ( Stringize Operator )

매크로 함수를 선언할 때 파라미터를 선언할 수 있으나 자료형은 사용할 수 없다. 매크로 파라미터를 사용할 때 파라미터로 전달한 값이 실제 코드에서 문자열 상수로 사용되기를 원한다면 전처리 문자열화 연산자( '#' )를 사용해야 한다. # 연산자는 단순하게 전달된 파라미터의 양쪽에 따옴표(" ")를 붙여서 문자열 상수로 변환한다. #연산자는 매크로 함수 안에서 파라미터를 문자열로 변환할 때만 사용된다


매크로 함수와 매크로 연속 연산자, 문자열화 연산자 사용 예

#define MSG(a, b) \

printf("param1=" #a", param2=" #b "\n");


혹은 아래처럼

#define MSG(a, b) \

printf("param1=%s, param2=%s \n", #a, #b);



토큰 결합 연산자 (##), Token Pasting Operator

매크로 연산자 '##' 은 좌우의 토큰을 단순히 결합하는 기능을 한다. 좌우의 토큰이 반드시 매크로의 파라미터일 필요는 없다

#include <stdio.h>

#define showNum(n) printf("num" #n "=%d \n", num##n)

int main()
{
	int num1 = 100;
	showNum(1);
	return 0;
}

위의 매크로 함수가 사용될 때 전처리기에 의해 변환되면 다음과 같은 형태가 될 것이다

printf("숫자=%d \n", num1);

그러므로 위의 문장이 실행되기 전에 num1 이라는 정수 변수가 존재해야 오류를 방지할 수 있다



전처리 defined() 연산자

전처리 조건( #if )을 구성할 때 사용되어 아규먼트로 전달된 매크로가 #define 에 의해 선언되어 있으면 true를 리턴한다

#if !defined( STDLIB )

#define STDLIB

#include <stdlib.h>

#endif



매크로 함수의 다른 예

#include <stdio.h>

#define SWAP(a,b) \
{int c=a; a=b; b=c;}

int main()
{
	int x = 10;
	int y = 20;

	SWAP(x, y);

	printf("x=%d, y=%d \n", x, y);

	return 0;
}


매크로 함수를 작성할 때 주의할 점

아래의 프로그램에 포함된 매크로 함수를 실행하면 원하는 결과가 출력되지 않는다

#include <stdio.h>

#define SQUARE(n) n*n

int main()
{
	int res = SQUARE(3);
	printf("SQUARE(3)=%d \n", res);  // 9

	res = SQUARE(3+2);
	printf("SQUARE(3+2)=%d \n", res);  //11

	return 0;
}

위의 매크로 함수는 다음과 같이 수정되어야 제대로 작동하게 된다

#define SQUARE(n) ((n)*(n))