학습자료(~2017)/C,C++

[C/C++] 매크로 - 퍼온 자료임

단세포소년 2012. 1. 12. 12:58
반응형

C/C++ 에서 #define 을 사용을 가급적 자제하도록 권고되고 있지만, 잘 사용할 경우 많은 코딩상의 이득을 볼 수 있습니다.  

#define을 활용하는 예를 몇 가지 설명하려고 합니다.

 

우선 팁을 설명하기 전에 간단하게 ‘##’ “#’ 에 대해 살펴보고 가겠습니다. (샘플소스는 MSDN 에서 가져왔습니다)

 

1.     ‘##’ - Token-Pasting Operator (##): 분리되어 있는 2개의 토큰을 하나로 뭉쳐주는 역할을 합니다.

#define paster( n ) printf( "token" #n " = %d", token##n )


라고 할 때 아래와 같은 코딩은

int token9 = 9;

paster( 9 ); // token##n -> token 과실제인수9 를합쳐token9 가됨


다음과 같이 풀이되어 결국 ‘token9 = 9’ 가 화면에 표시되게 됩니다.

printf( "token" "9" " = %d", token9 );

printf( "token9 = %d", token9 );


이와 같이 ## 은 왼쪽 Operand 와 오른쪽 Operand를 접착제처럼 붙여 주는 역할을 합니다.

 

2.     ‘#’ - Stringizing Operator (#): 매크로 인자를 문자열로 만드는 역할을 합니다.

#define stringer( x ) printf( #x "\n" )


과 같이 정의 할 경우 아래와 같은 문장은

stringer( In quotes in the printf function call\n );


다음과 같이 풀어쓰게 됩니다.

printf( "In quotes in the printf function call\n" "\n" );

 

이제 #define 매크로를 이용한 활용예를 들어 보겠습니다.

1.    배열의 크기 구하기


#define 을 이용하는 가장 간단하고 자주 쓰이는 팁이 아닐까 생각됩니다.

#define arrayCnt( x ) sizeof(x) / sizeof(x[0])


 이를 이용한 간단 샘플입니다.

struct LIST_COL_DEF

{

           int nChar;

           char szText[20];

} ListCol[] =

{

           {20, "폴더"},

           {20, "파일명"}

};

for (int i = 0; i < arrayCnt(ListCol); i++)

{

           // To Do Something...

}

 

물론

i < arrayCnt(ListCol)

를 사용하지 않고

i < sizeof(ListCol) / sizeof(ListCol[0])

을 사용해도 되지만 이와 같은 작업을 여러 번 반복할 경우(array 를 사용하는 경우는 정말 흔하디 흔하죠) 단조로운 코딩이 반복될 수 있어 #define 을 이용한 매크로를 애용하고 있습니다.

 

2.    Get/Set 함수 간단하게 만들기

OOP(Object Oriented Programming)로 프로그램 할 경우 멤버 변수의 Get 함수와 Set 함수를 만들어야 할 경우가 많습니다. 변수를 외부로 드러내지 않고 변수에 대한 접근 Method 만 제공함으로써 추후 변경에 대한 유지보수를 최소화하기 위해 많이 사용하게 되는데요(OOP 의 특징 중에 하나인 Encapsulation). 간단한 Get/Set 함수라 하더라도 하드코딩일 경우 손이 많이 가게 됩니다. 이럴 경우 #define 매크로를 사용하면 단순반복적인 Get/Set 함수를 한 줄만으로 코딩하여 작성할 수 있습니다.


예를 들어 다음과 같은 매크로를 정의한 경우

// SIMPLE_FUNC_IMPL -> Get/Set 함수를자동으로만들어줍니다.

// 1. ret -> return type

// 2. fname -> Get/Set 다음에올함수명

// 3. var -> Get/Set 에대상이되는변수명

#define SIMPLE_FUNC_IMPL(ret, fname, var) \

           ret Get##fname() \

           { \

                     return var; \

           } \

           void Set##fname(ret tmp) \

           { \

                     var = tmp; \

           }

 

bool m_bTest Get 하고 Set 하는 간단한 함수는 아래 한 줄로 만들 수 있게 됩니다.

SIMPLE_FUNC_IMPL(bool, Test, m_bTest);


위 한 줄의 매크로는 자동으로 전 처리기에 의해 컴파일 시 다음과 같은 함수로 확장됩니다.

bool GetTest()

{

           return m_bTest;

}

void SetTest(bool tmp)

{

           m_bTest = tmp;

}


간단하지 않은가요 ^^.

외부로 공개해야 하는 속성이 많으면 많을수록 코딩부담을 줄이고, 미스타이핑으로 인한 오류를 사전에 막아 줄 수 있어 매크로는 더욱 위력을 발휘하게 됩니다.

사실 마이크로소프트의 ATL 3.0 라이브로리 소스 헤더를 분석해 보면 이와 같은 매크로를 훨씬 복잡하게 사용하고 있다는 걸 알 수 있습니다.

아래 샘플은 ATL 3.0 라이브러리 ATLCTL.H 소스에 있는 코드입니다. (소스가 길어 접습니다

 

 끝으로, 이와 같은 #define 매크로의 단점도 잠시 언급하면

-       디버깅이 힘듭니다. 디버깅 시 매크로의 경우 확장된 소스가 보여지지 않아 변수의 변경사항을 추적해 들어 가기가 힘듭니다. 매크로가 복잡할 경우 애로가 있을 수 있어 복잡한 함수의 경우는 위와 같은 방식을 취하지 않는 편이 좋습니다.

-       #define MAX_CUSTOMER 10 보다는
const int MAX_CUSTOMER = 10
으로 코딩 하는 것이 변수가 잘못 typecasting 되는 걸 막을 수 있는 좋은 코딩 습관입니다. 사실 이 부분 때문에 #define 매크로사용을 자제해야 한다고 권장한다고 봐야겠죠.

 

이상으로 #define 매크로에 대해 아주 간단한 팁을 알아봤습니다. 사실 초보자가 아닌 경우 대부분 아실 만한 내용이라 적기가 부끄러운 내용이긴 한데 기록해 둘 경우 나중에 기억이 가물가물해 졌을 때나, 프로그램을 시작하는 다른 분들에게 도움이 될까 하고 적어봤습니다. 오늘도 열코!!

출처 : http://eslife.tistory.com/159






C언어를 다년간 사용한 프로그래머들도 종종 매크로 연산자에 대해서 정확하게 이해하고 있지 못한 경우가 많다. 매크로를 사용하지 않는다고 대답할 수도 있지만, 이미 복잡한 매크로를 사용한 수많은 소스가 있다는 점을 생각해 본다면 분명하게 이해해 두는 것이 정신 건강에 좋을 것이다. 복잡한 매크로 표현 식을 구성하는데 많이 사용되는 방법에 대해서 살펴보자.

첫째, 문자열 리터럴은 합쳐진다. 이는 매크로라기 보다는 C언어의 특징이다. 이 기능을 사용하면 긴 출력 문장을 손쉽게 여러 개의 부분 문자열로 나눌 수 있다. 또한 다음에 소개될 매크로 연산자를 사용할 때의 표현식도 좀 더 풍부하게 구성할 수 있다는 장점이 있다.

  1. printf("이름: %s\n"  
  2.        "나이: %d\n"  
  3.        "전화번호: %s\n", a, b, c);  


위의 코드를 보자. printf 다음에는 총 세 개의 연속된 문자열 리터럴이 나타난다. 이 세 개의 리터럴은 합쳐져서 "이름: %s\n나이: %d\n전화번호: %s\n" 과 동일한 문자열이 된다.

둘째, ## 연산자를 사용해서 토큰을 합성해서 만들어 낼 수 있다. ##은 합치기 연산자 이다. 다음과 같은 기능을 생각해 보자. COUNT(start)라고 선언을 하면 DWORD startCnt; 라는 변수를 선언하는 기능이다. 이를 매크로를 이용해서 만들어 보면 아래와 같다.

  1. #define COUNT(val) DWORD val##Cnt  


셋째, # 연산자는 전달된 인자를 문자열로 변환시킨다. start를 매크로 인자로 전달했다면 "start"가 된다는 말이다. 변수 값을 출력하는 매크로를 생각해 보자. PRINT(start)를 하면 화면에 start = 3과 같은 형태로 출력하고 싶은 경우다. 이럴 땐 아래와 같이 매크로를 만들면 된다. #연산자와 위에서 소개한 문자열 리터럴이 합쳐진다는 점을 이용한 것이다. 좀 꽁수 같이 보인다면 두 번째 방식같이 구성할 수 도 있다.

  1. #define PRINT(val) printf(#val " = %d", val)   
  2. #define PRINT(val) printf("%s = %d", #val, val)  


넷째, #@ 연산자는 전달된 인자를 문자로 변환시킨다. a를 매크로 인자로 전달했다면 'a'를 만들어 주는 것이다. 아래와 같이 전달된 인자에 대한 문자를 생성해주는 매크로를 예로 들 수 있다.

  1. #define makechar(val) #@val  


다섯째, 매크로의 내용이 복잡하고 한 줄 이상의 표현식이 필요한 경우엔 주로 do ~ while(0)문을 사용한다. 이렇게 하는 이유는 do ~ while(0)가 하나의 구문으로 해석되고 자체 블록을 가지기 때문이다. 이것을 단순 괄호({, })로 대체하면 안 된다. 중첩 if문에서 에러가 나기 때문이다. 주로 아래와 같이 사용한다.

  1. #define COMPLEX_MACRO(a,b,c,d)  do { \   
  2.                int complex_variable; \   
  3.                                       // some processing; \   
  4. while(0)  


여섯째, 릴리즈 버전에서는 무시되는 매크로를 구성하는 경우다. 주로 디버깅 출력을 하는 매크로가 여기에 속한다. Visual C++ 6.0에서는 '?' 연산자를 사용해서 쇼트 서킷을 구성하는 방법을 주로 사용했다. 하지만 이 후 출시된 Visual C++에는 __noop이라는 내장 함수를 가지고 있다. 이 함수는 인자를 모두 무시하는 기능을 한다. 따라서 예전에 복잡한 쇼트서킷을 사용했던 함수를 두 번째와 같이 간단하게 구성할 수 있다.

  1. #define TRACE 1 ? 0 : OutputDebugString   
  2. #define TRACE __noop  




출처 : http://www.jiniya.net/tt/528







 1. 전처리기에 의한 매크로 처리

(1) 전처리기에 의한 전처리

- 실행 파일 생성 순서

1) 프로그램 작성

2) 전처리기 (Preprocessor) 에 의한 전처리

3) 컴파일러 (Compile) 에 의한 컴파일

4) 링커 (Linker) 에 의한 링크

5) 실행 파일 생성

- 전처리 : # 으로 시작하는 문장의 처리 과정.

 

(2) #define 전처리기 지시자 (Preprocessor Directive)

- 전처리기 지시자 : # 으로 시작하는 문장. 전처리기 지시자에는 대표적으로 헤더파일을 포함시키는 문장 등 여러 종류가 있다.

1) #define 전처리기 지시자

- 단순 치환 작업을 요청할 때 사용된다.

- 매크로 선언 형식

#define 매크로  대체리스트

#define  PI  3.1415

* 매크로 선언에는 세미콜론 (;) 을 붙여주지 않는다.

* 매크로는 보통 대문자로 표기한다.

- 소스코드에 존재하는 매크로를 대체리스트로 컴파일 되기 이전인 전처리 과정 때에 치환한다.

- 선언 된 매크로가 상수로 치환될 경우, 이를 매크로 상수라 한다.

 

2) 매크로의 특징

ㄱ. 매크로 선언 시 대체리스트에는 다양한 문장이 올 수 있다.

ㄴ. 이미 선언된 매크로를 다른 매크로 선언에서 대체리스트에 사용할 수 있다.

ㄷ. 매크로 선언 시 대체리스트 영역에는 공백도 존재할 수 있다.

ㄹ. 소스코드 내에서 매크로가 대체리스트로 치환 되는 것은 문자열 문장 내에서는 불가능하다.

2. 매크로를 이용한 함수의 구현

(1) 매크로 함수

- 매크로는 매크로 인데 함수의 기능을 하는 매크로를 말한다. 함수처럼 인자를 전달 받을 수도 있다. 즉 매크로를 SQUARE(x) 와 같은 형식으로 선언한다. x 는 전달받을 인자를 말한다.

1) 특징

- 매크로 함수는 전처리기에 의해 단순 치환 방식으로 구현되므로 전달 인자의 자료형을 명시할 필요가 없기 때문에 자료형에 독립적이다.

 

2) 장점

- 함수 호출 때문에 발생하는 성능의 저하가 일어나지 않아 실행 속도가 향상된다.

 

3) 단점

- 소스코드 내의 매크로 함수 호출 부분이 매크로 함수 몸체 부분으로 완전 치환되므로 소스코드의 크기는 커질 수 밖에 없다.

- 매크로 함수를 정의하는 것은 일반 함수를 정의하는 것보다 어렵고, 따라서 오류 발생의 소지가 높다.

 

4) 필요성

- 함수의 크기가 작을 때 매크로 함수를 이용하면 실행 속도 향상의 이점을 얻을 수 있다.

 

5) 주의사항

- 매크로 함수의 몸체 부분에 사용되는 전달 인자는 무조건 소괄호 () 로 묶어 주어야 한다. 왜냐하면 치환되어 연산될 시 의도하지 않게 연산자 우선순위에 의해 잘못된 연산이 일어날 수 있기 때문이다. 또한 대체리스트 전체를 소괄호 () 로 묶어주는 것도 필요하다.

 

6) # 을 이용한 매크로 함수 전달 인자의 문자화

- 매크로에 의한 치환은 문자열 내에서는 이루어지지 않는다고 하였다. 그러나 C 언어 문자열 상수를 이어서 선언할 수 있다는 특징과 # 을 이용하면 이 또한 가능하다.

- EX>

#include <stdio.h>

#define ADD(x, y) printf( #x "+" #y "=%d \n", x+y)

 

int main()

{

ADD(3, 4);

return 0;

}

- 위와 같이 매크로 함수를 이용하여 완전한 printf 문장을 치환할 수 있다.

 

7) ## 을 이용한 토큰의 결합

- ## 은 매크로 함수에서 토큰 (컴파일러가 인식하는 의미를 지니는 문자나 문자열의 최소 단위) 을 서로 결합하는 때에 사용된다.

- EX>

#define FUN3(x, y) x##y

  로 선언된 매크로 함수는

FUN3(a, b) => ab

FUN3("AB", "CD") => "AB""CD"

FUN3(arr,[i]) => arr[i]

  와 같은 형태로 치환 된다.

- 실제 ## 은 변수나 함수의 이름을 동적으로 작성하기 위한 용도로 사용이 되는데 이는 앞으로 윈도우즈 관련 프로그래밍 (win32, API, MFC) 을 할 때에 사용 용도를 알 수 있다.

3. 이미 정의되어 있는 표준 매크로

- C 언어에는 정의하지 않아도 기본적으로 정의되어 있는 매크로가 존재하는 데 이를 표준 매크로라 한다. 아래는 자주 사용되는 표준 매크로들이다.

1) _FILE_ : 현재 소스 코드의 파일명을 나타내는 문자열

2) _TIME_ : 컴파일 시각을 "시:분:초"의 형태로 나타내는 문자열

3) _DATE_ : 컴파일 날짜를 "월 일 년"의 형태로 나타내는 문자열

4) _LINE_ : 현재 처리중인 소스파일의 행 번호를 나타내는 문자열

 

추가정보

http://www.ebyte.it/library/codesnippets/WritingCppMacros.html

출처 : http://blog.naver.com/graywolf82?Redirect=Log&logNo=60056482637







[C] 매크로의 또 다른 함정

C언어에서 자주 사용하는 매크로는,

그 편리함으로 자주 사용되면서도, 이로 인해 야기되는 문제점들 또한 자주 언급되고 경계의 대상이 되곤한다.


매크로의 사용시에 문제가 되는 대표적인 사례는,

연산대상 및 연산순서의 혼란으로 인한 문제들이 있다.

그리고 이런 오류의 가능성을 피하기 위해, 괄호를 적극적으로 활용하게 되곤 한다.



그런데, 매크로를 사용하는 경우에 발생할 수 있는 새로운 오류의 유형을 발견하여 향후에도 주의를 하고자 한다.



위의 예는, 실제의 코딩한 부분을 간단하게 바꾸고, 문제가 되는 부분만을 추려낸 것이다.

문자열로 나열된 이진수를 하나씩 숫자로 바꾸어 가는 과정이라고 생각하면 되겠다.

매크로 CH2NUM()은 기존에 알려진 유형의 문제점을 피하기 위해 가능한 괄호를 많이 사용했고, 특히 인자가 되는 부분을 단독으로 괄호로 묶음으로써 연산자 우선순위에 의한 오류를 배제하였다.


실제 저러한 코드를 수행하였을 경우에 제대로 된 결과가 나오지 않았다.

과연 문제가 무엇이었을까?


저 경우의 문제는 후위연산자인 "++"가 문제가 되었다.

물론 후위연산자가 모든 매크로에서 문제가 되는 것은 아니고,

여기에 사용된 매크로가 인자인 "ch"를 두번 참조하게 되었던 것과 결합하게 됨으로써 문제를 만들어냈던 것이다.


컴파일 되기 직전에 저 매크로로 대치된 소스의 모양은 어떻게 바뀌었는지 상상을 해보면 문제가 무엇인지는 명확해진다.



!!


매크로의 유혹이 거부하기 힘들다면,

매크로의 함정들을 모두 숙지해야만 할 것이다.



사실 매크로에 의해 야기되는 버그를 잡기란 쉽지 않다.

마치 파이썬, 펄, 쉘스크립트를 디버깅해야 하는 상황처럼 불편하고 답답하기 짝이 없다.


그렇다고 매크로에 의해 작성된 코드가 좀 더 가벼울까?

함수가 가지는 스택 오버헤드는 줄일 수 있지만, 꼭 그렇다고 말할 수도 없다.


매크로는 지극히 인간의 편의, 그것도 해석하는 사람보다는 만드는 사람의 편의를 위한 도구임이 틀림없어 보인다

출처 : http://blog.daum.net/dimalion/7770265









유용하게 사용하는 매크로 함수

#include <stdio.h>
// 최대값 구하기
#define MAX(a,b) ( (a) > (b) ) ? (a):(b)
// 대문자로
#define UPCASE(c) (( (c)>='a' && (c)<='z') ? (c)-('a'-'A') : (c) )
// 소문자로
#define LOWCASE(c) (( (c)>='A' && (c)<='Z') ? (c)+('a'-'A') : (c) )
// 행 분리 시에는 '\' 사용
#define ASSERT(x) if (!(x) ) \
                  {          \
                     printf("assert failed. %s(%d) at %s %s(%s)\n", __FILE__, __LINE__, __DATE__, __TIME__, __TIMESTAMP__); \
                  }
// #은 "" 처럼 동작
#define DPRINT(expr) printf(#expr " = %g\n", expr )
// ##은 문자 붙이기
#define TPRINT(expr) printf("%d, %d\n", expr##1, expr##2)
void main()
{
 printf("%d\n", MAX(5,3) );
 printf("%g\n", MAX(5.4,3.2) );
 printf("%c\n", UPCASE('a'));
 printf("%c\n", LOWCASE('A'));
 ASSERT(0);
 double x = 3.4;
 double y = 2.0;
 DPRINT(x/y);    // printf("x/y" " = %g\n", expr)
 int a1 = 10, a2 = 20;
 TPRINT(a);
}


출처 : http://www.iamcorean.net/141









1. SAFE_ 매크로

Direct3D 에서 제공하는 유용한 매크로로써 DirectX SDK 의 Framework 에서 찾음

  • //-----------------------------------------------------------------------------
    // Miscellaneous helper functions
    //-----------------------------------------------------------------------------
    #define SAFE_DELETE(p)       { if(p) { delete (p);     (p)=NULL; } }
    #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p);   (p)=NULL; } }
    #define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }
    

2. 조건문 매크로

조건문을 좀 더 명시적으로 나타내기 위하여 만든 매크로.

  • // 조건문 매크로
    #define IS_TRUE(c)              (c)
    #define IS_FALSE(c)             (!(c))
    
    #define IS_EXIST(c)             (c)
    #define IS_NOT_EXIST(c)         (!(c))
    
    #define IS_VALID(c)             (c)
    #define IS_INVALID(c)           (!(c))
    
    #define IS_SUCCEEDED(c)         (c)
    #define IS_FAILED(c)            (!(c))
    
    // 포인터 매크로
    #define IS_NULL(p)              (!(p))
    #define IS_NOT_NULL(p)          (p)
    
    #define IS_ZERO(n)              (!(n))
    #define IS_NOT_ZERO(n)          (n)
    
    // 범위 검사 매크로
    #define IS_WITHIN(min,max,expr)         (((min)<=(expr))&&((max)>=(expr)))
    #define IS_WITHOUT(min,max,expr)        (((min)>(expr))||((max)<(expr)))
    

3. STL 매크로

STL 을 사용할 때 자주 사용하는 코드를 매크로로 만듬.

  • // EMPTY 매크로
    #define IS_EMPTY(c)             ((c).empty())
    #define IS_NOT_EMPTY(c)         (!((c).empty()))
    
    #define IS_EMPTY_PTR(p)         ((p)->empty())
    #define IS_NOT_EMPTY_PTR(p)     (!((p)->empty()))
    
    #define WHILE_NOT_EMPTY(c)      while(!((c).empty()))
    
    // FOUND 매크로
    #define IS_FOUND(i,c)           (i != (c).end())
    #define IS_NOT_FOUND(i,c)       (i == (c).end())
    
    #define IS_FOUND_PTR(i,p)       (i != (p)->end())
    #define IS_NOT_FOUND_PTR(i,p)   (i == (p)->end())
    
    // FOREACH 매크로
    #define FOREACH_MAP(i,c)        for( i = (c).begin(); i != (c).end(); ++i )
    #define FOREACH_MAP_PTR(i,c)    for( i = (c)->begin(); i != (c)->end(); ++i )
    
    #define FOREACH_VECTOR(i,c)     for( i = 0; i < (c).size(); ++i )
    #define FOREACH_VECTOR_PTR(i,c) for( i = 0; i < (c)->size(); ++i )
    
    // CLEANUP 매크로
    #define CLEANUP_MAP(i,c)        for( i = (c).begin(); i != (c).end(); ++i ) \
                                            delete (*i).second; \
                                    (c).clear();
    #define CLEANUP_MAP_PTR(i,c)    for( i = (c)->begin(); i != (c)->end(); ++i ) \
                                            delete (*i).second; \
                                    (c)->clear();
    
    #define CLEANUP_VECTOR(i,c)     for( i = 0; i < (c).size(); ++i ) \
                                            delete c[i]; \
                                    (c).clear();
    #define CLEANUP_VECTOR_PTR(i,c) for( i = 0; i < (c)->size(); ++i ) \
                                            delete c->at(i); \
                                    (c)->clear();
    

4. Breakpoint 매크로

Visual Studio 에서 강제로 Breakpoint 를 설정해야 할 경우 유용하게 사용할 수 있다. Win32 API 를 사용할 경우에는 DebugBreak() 함수를 사용할 수도 있지만 VC++ 만 사용한다면 intrin.h 헤더 파일을 포함한 후 __debugbreak() 함수를 사용하면 된다. 하지만 지원 안되는 버전의 VC++ (Visual Studio 2003 이하) 일 경우 다음과 같이 간단히 선언 후 사용할 수 있다.

  • #if (_MSC_VER >= 1400)
            // Microsoft Visual C++ .NET 2005 이상
            #include <intrin.h>
    #else
            // Microsoft Visual C++ .NET 2003 이하
            #define __debugbreak()                  {__asm int 3}
    #endif
    
    
  • 응용 코드
    #ifdef _WINDOWS
            #define __debugbreak()  { DebugBreak(); }       // Windows 모드
    #else
            #define __debugbreak()  {__asm int 3}           // Console 모드
    #endif  // _WINDOWS
    
    

5. 컴파일러 TODO 매크로

5.1. _TODO.h 파일

  • // _TODO.h : TODO / FIXME / NOTE 정의
    //
    
    //------------------------------------------------------------------------------
    // TODO / FIXME / NOTE macros
    //------------------------------------------------------------------------------
    
    #define _QUOTE(x)               # x
    #define QUOTE(x)                _QUOTE(x)
    #define __FILE__LINE__          __FILE__ "(" QUOTE(__LINE__) "): "
    
    #define NOTE( x )               message( x )
    #define FILE_LINE               message( __FILE__LINE__ )
    
    #define TODO( x )               message( __FILE__LINE__"[TODO]: " #x "\n" )
    #define FIXME( x )              message( __FILE__LINE__"[FIXME]: " #x "\n" )
    
    

5.2. 매크로 사용 예

  • 소스 코드
    // main.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의
    //
    
    #include "_TODO.h"
    
    // main() 함수 구현
    int main()
    {
    #pragma TODO( TODO 메시지 1...! )
    #pragma FIXME( FIXME 메시지 2...! )
    
    #pragma FILE_LINE
    #pragma NOTE( "NOTE 메시지 3...!\n\
            ------------------------------------------------------------")
    
            return 0;
    }
    
    
  • 컴파일 결과
    ------ 빌드 시작: 프로젝트: ExMacro, 구성: Debug Win32 ------
    
    컴파일하고 있습니다.
    main.cpp
    d:\exmacro\main.cpp(9): [TODO]: TODO 메시지 1...!
    d:\exmacro\main.cpp(10): [FIXME]: FIXME 메시지 2...!
    d:\exmacro\main.cpp(12):
    NOTE 메시지 3...!
      ------------------------------------------------------------
    링크하고 있습니다.
    
    빌드 시간 0:00
    빌드 로그가 "file://d:\ExMacro\Debug\BuildLog.htm"에 저장되었습니다.
    ExMacro - 0 오류, 0 경고
    
    
    ----------------------완료----------------------
    
        빌드: 성공 1, 실패 0, 생략 0
    
    

6. ASSERT(), VERIFY(), and TRACE() 매크로

ASSERT() 와 VERIFY(), TRACE() 매크로는 MFC 에서 사용하는 매크로로써 디버깅에 매우 유용하다. 하지만 MFC 을 사용하지 않는 프로그램에서 사용할 수 없다. 그러나 이 매크로들을 사용할 수 있는 방법을 아래의 글에서 찾을 수 있다.

6.1. debug.h 파일

  • // file debug.h
    
    #ifndef __DEBUG_H__
    #define __DEBUG_H__
    
    #ifdef _DEBUG
    void _trace(char *fmt, ...);
    #define ASSERT(x)       {if(!(x)) _asm{int 0x03}}
    #define VERIFY(x)       {if(!(x)) _asm{int 0x03}}
    #else
    #define ASSERT(x)
    #define VERIFY(x)       x
    #endif
    
    #ifdef _DEBUG
    #define TRACE           _trace
    #else
    inline void _trace(LPCTSTR fmt, ...) {}
    #define TRACE           1 ? (void)0 : _trace
    #endif
    
    #endif // __DEBUG_H__
    

6.2. debug.cpp 파일

  • // file debug.cpp
    
    #ifdef _DEBUG
    
    #include <stdio.h>
    #include <stdarg.h>
    #include <windows.h>
    
    void _trace(char *fmt, ...)
    {
            char out[1024];
            va_list body;
            va_start(body, fmt);
            vsprintf(out, fmt, body);
            va_end(body);
            OutputDebugString(out);
    }
    
    #endif
    


출처 : http://www.viper.pe.kr/cgi-bin/moin.cgi/C%2B%2B_%EC%9C%A0%EC%9A%A9%ED%95%9C_%EB%A7%A4%ED%81%AC%EB%A1%9C

 




씹어먹는 C 언어 - <21. 매크로 함수, 인라인 함수>
 - 이 강좌는 그림이 많은 관계로 출처를 통해 원문을 보시오.
출처 : http://itguru.tistory.com/99









9. 조건부 컴파일

조건부 컴파일이란 특정한 조건이 성립될 때나 또는 성립되지 않는 경우에만 지정한 범위의 문장을 컴파일 하거나 컴파일 하지 않게 하는 것을 말합니다.

이는 특정한 조건이 아닌 여러 상황에 맞게 컴파일 되어 프로그램의 이식성이 높아지게 합니다.

 
 

1) #if #else #endif

#if #else #endif 사용형식

#if 조건식

명령문1

#else

명령문2

#endif 

조건식을 만족하게 되면 명령문1을 수행하고 만일 조건식을 만족하지 못하면 명령문2를 수행합니다.

 
 

8-15 실습예제 (조건부 컴파일을 이용한 프로그램)

#include<stdio.h>

#define JUMSU 50

#if (JUMSU >= 100)

#define JUMSU1 100

#else

#define JUMSU1 0

#endif

int main()

{

printf("%d\n",JUMSU1);

return 0;

}

0  

위의 프로그램에서 JUMSU가 50으로 정의 되어 있으므로 else 다음 문장을 컴파일 하게 됩니다.

따라서 0이 출력됩니다.

만일 JUMSU를 100 이상의 숫자로 정의 하게 되면 100이 출력되게 될 것입니다.

2) #ifdef #else #endif

#if #else #endif 사용형식

#ifdef MACRO(조건문)

명령문1

#else

명령문2

#endif 

 
 

조건문에 해당하는 매크로이름이 있다면 명령문1을 수행하고 만일 없다면 명령문2를 수행하게 됩니다.

 
 

3) #ifndef #else #endif

#ifndef #else #endif 사용형식

#ifndef MACRO(조건문)

명령문1

#else

명령문2

#endif 

 
 

#ifdef 와 반대되는 기능을 하며 조건에 맞게 컴파일 하게 됩니다.

8-16 실습예제 (조건부 컴파일을 이용한 프로그램)

#include
<stdio.h>

#define MIN 100

int main()

{

    int i, j;

    i = -100;

    j = 0;

    printf("i = %d\n",i);

#ifdef MIN

     printf("MIN = %d\n",MIN);

#endif

 
 

#ifndef MIN

     printf("MIN = %d\n",MIN);

#else

     printf("MIN = -100\n");

#endif

     printf("j = %d\n",j);

     return 0;

}

i = -100

MIN = 100

MIN = -100

j = 0 

i = -100 이라는 문장을 먼저 출력하고 만일 MIN이라는 매크로가 있다면 MIN을 출력하라는 조건부 컴파일 문이 되고 다음 조건부 컴파일 문은 만일 MIN이라는 매크로가 없다면 MIN을 출력하고 그렇지 않다면 -100을 출력하라는 명령문을 수행하게 됩니다.

조건부 컴파일이 끝나게 되면 j를 출력하게 됩니다.

이처럼 조건부 컴파일은 특정한 상황에 맞게 컴파일 할 수 있도록 해 주는 기능을 가지고 있습니다.


출처 : http://killernet.egloos.com/2380095









[VC] 조건부 컴파일 지시어(#if, #elif, #else, #ifdef, #ifndef, #endif)

조건부 컴파일 지시어는 지정된 조건에 의하여 프로그램 파일의 특정한 부분을 컴파일하거나 컴파일하지 않게 하는 지시어이다. 조건부 컴파일 지시어의 의미를 알아 보자.

조건부 컴파일 지시어

의미

#if 조건

#if 다음에 조건이 맞으면 #if와 #endif 사이의 문장들을 컴파일한다.

조건에 맞지 않으면 #if와 #endif 사이를 컴파일 하지 않고 넘어간다. 

#elif 조건

#if, #ifdef, #ifndef의 조건에 맞지 않을 경우 또 다른 조건을 검색하기 위한 지시어로 사용된다.

#else 조건

#if, #ifdef, #ifndef의 조건에 맞지 않을 경우 #else와 #endif 사이의

문장들을 컴파일 한다.

#ifdef 문자열

문자열이 define 되었다면

#ifndef 문자열

문자열이 define 되지 않았다면

#endif

#조건부 컴파일 지시어를 마치게 해주는 지시어이다.

반드시 조건부 컴파일 지시어 마지막 부분에 있어야 한다.

다음은 #if 를 이용한 예를 보여준다. 문자열 “SEL”이 define 되었다면 “#define PI 3.14”을 컴파일하고 define 되었다면 컴파일하지 않는다.

#if defined SEL       

#define PI 3.14       

#endif                


다음은 #ifdef를 이용한 예를 보여준다. 문자열 “SEL”이 define 되었다면 “#define PI 3.14”을 컴파일하고 define 되었다면 컴파일하지 않는다.

#ifdef SEL       

#define PI 3.14       

#endif                


다음은 #ifndef를 이용한 예를 보여준다. 문자열 “SEL”이 define 되지 않았다면 “select_function()”을 컴파일하고 define 되었다면 컴파일하지 않는다.

#ifndef SEL       

select_function()   

#endif                


다음은 #if, #elif와 #else를 이용한 예를 보여준다. 문자열 “SEL1”이 define 되었다면 “select_function1()”을 컴파일하고 define 되지 않았다면 문자열 “SEL2”가 define 되었는지 확인하여 “SEL2“가 define 되었다면 ”select_function2()”을 컴파일하고 “SEL2"도 define 되지 않았다면 ”#define PI 3.14“을 컴파일한다.

#if defined SEL1

select_function1()

#elif defined SEL2

select_function2()

#else

#define PI 3.14

#endif                


다음은 #ifdef와 #else를 이용한 예를 보여준다. 문자열 “SEL”이 define 되었다면 “select_function()”을 컴파일하고 define 되지 않았다면 ”#define PI 3.14“을 컴파일한다.

#ifdef SEL

select_function()

#else

#define PI 3.14

#endif                


출처 : http://lachesis76.egloos.com/476484

 

반응형