ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 전처리기 지시문(Preprocessor directives) - 1
    프로그래밍 언어/C & C++ 2020. 6. 26. 23:19

    전처리기 지시문은 프로그램의 코드에 #로 시작하는 라인들을 말합니다.

    예를 들어 #include, #define 등이 있죠.

    이 코드들은 구문(statement)이 아니라 전처리기(preprocessor)에서만 사용되는 지시문입니다.

     

    전처리기는 실제로 컴파일을 하기전에, 전처리기 지시문을 읽어서 해당하는 지시문에 따라 처리를 해주는데요, 이 지시문은 구문과 다르게 ';' 로 끝나지 않고 newline 문자가 있으면 끝나는 것으로 봅니다. 전처리 지시문이 한 줄을 넘어가게 된다면 \ 를 입력하여 줄을 구분해주면 됩니다.

     

    코드 처리 과정

    전처리 지시문에는 아래와 같은 종류가 있습니다.

    - Macro definitions (#define #undef)

    - Conditional inclusions (#ifdef #ifndef #if #endif #else #elif)

    - Line control(#line)

    - Error directive(#error)

    - Source file inclusion(#include)

    - pragma directive(#pragma)

    * predefined macro names( __something__)

     

    1. Macro definitions (#define #undef)
    - #define
    이 지시문은 2가지 사용 방법이 있습니다. 

     

    식별자 치환 (identifier replacement)

    #define [identifier] [replacement]

    #define INF 1e9
    #define ZERO 0

     

    위와 같이 지시문을 사용하게 되면 identifier에 해당하는 내용을 모두 replacement로 바꿔줍니다.

    즉, 코드에서 INF를 발견했을 때 모두 1e9로 바꿔줍니다. 이런식으로 사용했을 때, 좀 더 코드를 직관적으로 볼 수 있도록 만들어줍니다. 

     

    함수 매크로(function macro)

    #define [function] [function define]

    #include <iostream>
    
    #define Max(a,b) ((a)>(b)?(a):(b))
    
    int main(){
    	int x = 5, y = 10;
    	std::cout << Max(x,y);
        return 0;
    }

    위와 같이 함수를 정의하여 코드에서 사용할 수 있습니다.

    함수 매크로에서는 2가지 특별한 연산기호를 사용합니다. #와 ##인데요,

    #은 매개변수 앞에 오는데요, #매개변수와 같이 define해주면, 해당 매개변수로 전달되는 값을 문자열로 바꿔줍니다.

    #include <iostream>
    #define str(x) #x
    int main(){
    	std::cout<<str(Hello World!);
    	std::cout<<"Hello World!";
    }

    ##은 두 매개변수를 중간에 빈칸이 없이 이어줍니다. 아래와 같이 std::c 와 out을 붙여서 std::cout으로 변환시킬 수 있습니다.

    #include <iostream>
    #define concatenate(a,b) a ## b
    int main(){
      concatenate(std::c,out) << "Hello World!";
    }

    - #undef

    undef를 사용할 시 define된 macro를 undefine해줍니다.

    #undef INF
    #undef ZERO
    #undef Max
    #undef str
    #undef concatenate

    2. Conditional inclusions (#ifdef #ifndef #if #endif #else #elif)

    - #ifdef #endif

    #ifdef는 #endif와 같이 사용합니다. #ifdef 다음에 있는 parameter가 정의가 되어있다면
    #ifdef #endif 사이의 부분을 코드에 포함 합니다.

    예를 들자면, 아래 코드는 INF가 정의되어 있다면 a에 INF에 해당하는 값을 할당하겠다고 하는 코드입니다. 이 부분은 INF가 정의가 되어있지 않다면 해당하는 부분을 코드를 포함하지 않기 때문에 아무 문제가 없습니다.

    하지만 INF가 정의가 되어있지 않다면 그 다음 줄에 b에 INF를 할당하는 부분은 error가 날 수도 있습니다.

    #ifdef INF
    int a = INF;
    #endif
    int b = INF; // Could be error!

    - #ifndef
    #ifndef는 #ifdef와 정확히 반대입니다. #if not defined #ifndef 다음에 있는 parameter가 정의되어 있지 않다면 #ifndef #ifdef 사이의 부분을 코드에 포함하지 않습니다. 

    아래 코드와 같이 define이 되어있지 않다면 새로 define하는 식으로 사용할 수 있습니다. 물론 이전에 정의가 되어있다면 해당하는 부분을 건너뛰기 때문에 새로 define하지는 않습니다.

    #ifndef INF
    #define INF 2e9
    #endif

     

    -#if #elif #else 

    이 지시문은 if문과 비슷하게 동작합니다. #elif 는 else if 와 비슷합니다.
    if문인데, 전처리기 지시문에서만 사용하는 조건문이라고 생각하시면 될 것 같습니다.

     

    어떤 정의된 NUM에 대해
    NUM 이 0보다 크면 1로

    NUM 이 0보다 작으면 -1로

    NUM이 0이면 undefine하는 코드를 작성한다고 하면 아래와 같습니다.

    #if NUM > 0
    #undef NUM
    #define NUM 1
    
    #elif NUM < 0
    #undef NUM
    #def NUM -1
    
    #else
    #undef NUM
    #endif

     

    * #if #elif 뒤에 들어갈 수 있는 상수 식(constant expression)이나 Macro로 구성된 식만 들어 갈 수 있습니다.

     

    이 때, 사용할 수 있는 defined , !defined라는 특별한 연산자가 있습니다.

    어떤 parameter가 정의되어있는 지, 정의되어있지 않은 지를 확인할 수 있습니다.  

    아래의 코드는

    NUM이 정의되어 있다면 undefine하고 0으로 정의.

    NUM이 정의되어 있지 않다면 바로 0으로 정의하는 코드입니다.

    #if defined NUM
    #undef NUM
    #define NUM 0
    
    #elif !defined NUM
    #define NUM 0
    
    endif

    *즉, #if defined 는 결국 #ifdef 와 동일합니다.

     

    이번 글에서는 전처리 지시문 중에 macro definition과 conditional inclusion 2가지를 다뤘습니다. 다음 글에서는 이 이후의 부분을 다뤄보려고 합니다. 틀린 부분이 있거나 오타가 있다면 댓글로 지적해주시기 바랍니다:)

    댓글

Designed by Tistory.