본문 바로가기
Basic/C/C++

[C언어] 제 20 강 : 매크로와 전처리기

by boxbop 2012. 1. 27.
반응형


 처음에 설명했던 부분 기억나시나요? 실행파일이 컴파일과 링크의 과정을 거쳐서 만들어지는 것으로 설명했습니다. 실제로는 컴파일 이전에 '전처리'라는 과정을 거치게 됩니다. 즉, 소스파일은 선행처리기에 의해 전처리 과정을 한번 거친 후에 컴파일 거치게 됩니다. 사실 선행처리기가 하는 일은 아주 간단합니다. 우리가 작성해 놓은 선행처리 명령문대로 소스코드의 일부를 수정하는 것이 전부입니다.

 지시자   매크로    매크로몸체

 위와 같이 선행처리 명령문은 기본적으로 세 부분으로 나뉩니다. 아래 문장을 예로들어 설명하겠습니다.

 #define A 10

지시자(#define)는 선행처리기에 이어서 등장하는 매크로를 마지막에 등장하는 매크로 몸체로 치환하라는 명령을 내립니다. 즉, A 를 10으로 치환하라는 의미입니다.
 
 ex) #define NAME "boxbop"
      #define AGE 100

 또한 define 매크로는 다음과 같은 형태로도 사용이 가능합니다.

 #define   FUNCTION(X)   X*X

 즉, FUNCTION(123) 이라는 문장이 나올때 123*123 의 형태로 치환하라는 의미입니다. 함수와도 비슷하죠~? 참고로 매크로를 정의할 때는 먼저 정의되어 있는 매크로도 사용이 가능합니다^-^

 그렇다면 매크로 함수의 장점에는 무엇이 있을까요~?

 1. 매크로 함수는 일반 함수에 비해 실행속도 빠르다
 2. 자료형에 따라서 별도로 함수를 정의하지 않아도 된다.

 알다시피 함수가 호출되면 호출된 함수를 위한 스택 메모리의 할당, 실행위치의 이동과 매개변수로의 인자 전달, return문에 의한 값의 반환등 복잡한 절차를 필요로 합니다. 그렇지만 매크로 함수는 이러한 과정이 불필요하다는 것 입니다^^ 하지만 정의하기가 까다롭기 때문에 무분별하게 사용해서는 안됩니다. 함수의 크기, 길이가 짧거나 호출이 되는 빈도수가 높은 함수를 매크로로 정의하면 좋습니다~

 <조건부 컴파일을 위한 매크로>

 매크로 지시자 중에는 특정 조건에 따라 소스코드의 일부를 삽입하거나 삭제할 수 있도록 디자인 된 지시가자 존재합니다.

 [#if ... #endif]

조건부 코드 삽입을 위한 지시자 입니다. #if문 뒤에는 반드시 #endif문이 존재해야 합니다. 예제를 보시면 이해가 빠르실 겁니당~

 #define CODE_A 1
 #define CODE_B 0

int main(void)
{
   int number1, number2;
   number1 = 10;
   number2 = 20;

  #if CODE_A
     number1 += number2;
  #endif

  #if CODE_B
    number1 -= number2;
  #endif

    return 0;
  }

CODE_A의 값이 참이라면 #if ~ #endif 문까지가 코드에 삽입되어 실행되고 그렇지 않다면 실행되지 않습니다. 즉 위의 예제에서는 CODE_A가 1이기 때문에 두개의 피연산자의 덧셈이 실행되고 CODE_B는 0이기 때문에 뺄셈코드는 삭제되어 무시되겠죠~?

 [#ifdef ... #endif]

 위의 지시자도 #if ... #endif 지시자와 비슷합니다. #if는 매크로가 참인지 거짓인지를 기준으로하여 작동하지만 #ifdef는 매크로가 정의되어 있는지 아닌지를 기준으로 동작합니다. 

 #define CODE_A 1
 //#define CODE_B 0  (주석처리)

int main(void)
{
   int number1, number2;
   number1 = 10;
   number2 = 20;

  #ifdef CODE_A
     number1 += number2;
  #endif

  #ifdef CODE_B
    number1 -= number2;
  #endif

    return 0;
  }

CODE_B를 주석처리했기 때문에 정의되어있지 않다고 판단하여 두 피연산자의 뺄셈은 생략이 되겠죠~? 사실상 코드에서 그냥 소멸되어 버립니다. 반면 CODE_A는 정의되어있기 때문에 덧셈코드는 정상적으로 작동하구요~

 [#ifndef ... #endif]

 위의 매크로는 정의되어있지 않!!!았다면 이라는 뜻입니다. #ifdef와는 반대의 개념이죠? 따로 설명하지는 않겠습니다~

 [#else의 삽입]

#if, #ifdef, #ifndef에서 모두 #else를 사용할 수 있습니다.

 #if CODE_A == 2
  .....
 #else
  .....
 #endif

 이런식으로 기존의 if문과 동일하게 사용할 수 있습니다. 그리고 if문에는 esle if문이 여러개 존재할 수 있죠? else if 역할을 하는 매크로도 존재합니다.

 [#elif]

 위 매크로가 기존의 else if 역할을 합니다. 당연히 여러번 사용할 수 있구요~

 #if CODE_A == 1
   .....
 #elif CODE_A == 0
   .....
 #elif CODE_B == 1
   .....
 #else
   .....
 #endif

그러나 #if와 #ifdef에는 한계가 존재합니다. 예를들어 매크로 NUMBER의 값이 5이고, 매크로 CODE가 정의되어 있으며, 매크로 COUNT의 값이 1이 아닌 경우에 어떤 문자열을 출력한다고 가정해봅시다~

 #define NUMBER 5
 #define CODE
 #define COUNT 2

int main(void)
{
#ifdef CODE
   #if COUNT != 1 && NUMBER == 5
          .....
   #endif
#endif

   return 0;
}

위와 같이 복잡하게 됩니다. 그렇다면 매크로의 참, 거짓과 정의의 유무를 한줄로 표현할 수 있는 것이 defined 연산자 입니다.

 [#if와 함께 사용할 수 있는 defined 연산자]

#if defined(CODE)
  .......
#endif

 defined 연산자는 괄호안의 매크로가 존재하는지 존재하지 않는지 판단합니다. 때문에 좀 전에 복잡했던 예제를 다음과 같이 표현할 수 있습니다.

#if defined(CODE) && COUNT != 1 && NUMBER ==5
  .....
#endif

 보기에도 좀 더 간단해 졌죠~? defined처럼 유용한 매크로 연산자가 존재합니다.

 [매크로 # 연산자]

#define   STR(ABC)   #ABC

 ->매개변수 ABC에 전달되는 인자를 문자열 "ABC"로 치환한다

 매크로 ## 연산자

 #define   MIXNUMBER(A, B, C)   A ## B ## C

 ->매개변수 A, B, C에 전잘되는 인자를 ABC 하나로 만들어준다

 만약 12, 23, 22를 전달하면 122322라는 새로운 수가 만들어집니다.
단순히 A ## B ## C를 ABC나 A B C로 사용해버리면 의도하지 않은 결과를 초례합니다~

 [그외 정의되어 있는 매크로들]

 __FILE__  :  현재 소스코드의 파일명을 나타내는 문자열
 __TIME__ :  선행처리 시작을 "시:분:초" 의 형태로 나타내는 문자열
 __DATE__ : 선행처리 날짜를 "월:일:년" 의 형태로 나타내는 문자열
 __LINE__ : 현재 소스코드의 행 번호를 나타내는 상수

 
ex)  printf("현재 행 : %d ", __LINE__);
        printf("파일 명 : %s ", __FILE__);

 [행 번호와 파일 이름을 지정하는 #line]

 단순히 행의 기준을 사용자가 원하는 기준으로 정하고, 전체경로를 포함한 파일의 이름이 아닌, 그냥 파일의 이름만 출력을 할 수 있도록 합니다.

 ex) #line  0
        ->현재 행 번호를 0으로 설정한다.
       #line  1  "test.c"
        ->현재 행 번호를 1으로 설정하고, 파일이름을 test.c로 설정한다.
반응형