2010년 1월 28일 목요일

매크로 상수

- 매크로 정의
: 매크로는 절대!! 연산이 아닌 단순한 문자열 치환일 뿐이다!!!
: #define PX printf("x is %d.\n",x) //공백으로 구분됨.

* #define : 선행처리 지시문
* PX         : 매크로
*
printf("x is %d.\n",x) : 보디

- 상수의 토큰

: 상수 정의시 토큰사이에 여러공백들이 있을시 선행처리기는 하나의 토큰으로 바꿔버린다. 문자열로 지정된 상수는 모든 공백도 값이로 취급한다.

#define FOUR 2*2   // 2*2는 사이 공백이 없이 붙어있는 하나의 토큰인 경우.
#define SIX 2 * 3    // 2 * 3은 사이 공백으로 구분된 토큰이 3개인 경우.

- 상수 재정의
: 새로운 정의가 기존의 정의와 동일한 순서와 토큰을 가져야한다.

#define SIX 2 * 3
#define SIX 4        *         5

위 와 같은 경우는 상수 재정의가 가능하다.
그러나 다음의 경우는 해당되지 않는다.

#define SIX 4*5  // 토큰이 하나이고 위는 토큰이 3개이므로 재정의불가.

- #define에서의 인수 사용
: 함수같은 매크로 생성 가능
#define MEAN(X,Y) (((x)+(Y))/2)

* MEAN() : 매크로
* X,Y       : 매크로 인수
*
(((x)+(Y))/2) : 대체 보디

#define SQUARE (X) X * X  // 다음과 같이 호출이 가능하다.
z = SQUARE(2);                //인수로 넘겨진 값의 제곱을 구하는 함수 매크로

ex) 매크로는 문자열을 치환한다.
#define SQUARE(x) x*x
int x = 4;
int z;
z= SQUARE(x + 2) // 이경우 6이란 인수가 넘어가는게 아니다.
                          // 인수로 x+2가 넘어가서 결과적으로 x+2*x+2가 계산되어
                               14라는 값을 리턴하게 된다.
일반 함수는 실행되는 동안 처리 되고 매크로 호출은 컴파일 하기 전에 인수 토큰을 프로그램으로 보낸다. 위 의 경우 36이란 값을 얻을려면
#define SQUARE(x) (x)*(x)로 하면 된다. 그러면 (x+2)*(x+2)가 되므로 괄호연산자에 의해 원하던 결과값을 얻을 수 있을 것이다.

또 다른 경우
100/SQUARE(2)라고 되어있으면 치환된값은
100/2*2가 된다. /와 *는 연산자 우선순위가 같으므로 왼쪽에서 오른쪽으로 계산이 될것이다. (100/2)*2가 되는 셈이다. 이경우 100/(2*2)가 되어야 원하는 결과를 얻을 수 있을것이다.
#define SQUARE(x) (x*x)와 같이 선언이 되어 있어야 한다.
위의 두 경우를 모두 커버 하기 위해서는
#define SQUARE(x) ((x)*(x)) 라고 선언해야 된다.

또 SQUARE(++x)라고 함수 호출이 되면
++x*++x  == 5*6  (연산자 우선순위가 높은 ++연산 부터 수행 되어 * 앞에서한번 * 뒤에서 한번하여 4라는 값이 위와같이 변경되는 경우이다.

결론적으로 #define 매크로 함수 호출을 사용하여야 하는경우 증감연산자는 인수로 사용하지 않는것이 좋다.

- 매크로 인수에서 문자열 생성하기 : #연산자
#include <stdio.h>
#define PSQR(x) printf("The square of " #x " is %d.\n", ((x)*(x)));
int main(void)
{
   int y = 5;
   PSQR(y);
   PSQR(2+4);
   return 0;
}

위 의 결과값은
The sqare of y is 25.
The sqare of 2 + 4 is 36.
즉, 넘겨지는 파라메터가 수치가 아닌 문자값 그대로 복사가 된다.
여기서 #x는 정해져 있는 값이 아니라 매크로 함수의 인자 파라메터와 같으면 된다.

- 선행처리기 결합 : ## 연산자
#include <stdio.h>
#defie XNAME(n) x ## n
#define PRINT_XN(n) printf("X" #n " = %d\n" x ## n);
int main(void)
{
   int XNAME(1) = 14;  // int x1 = 14
   int XNAME(2) = 20;  // int x2 = 20
   PRINT_XN(1);          // printf("x1 = %d\n", x1)
   PRINT_XN(2);          // printf("x2 = %d\n", x2)
   return 0;
}
// 실행 결과 값은 아래와 같다.
x1 = 14
x2 = 20
결과에서 보다시피 문자열을 더하고 그 결과값이 마치 변수를 생성하는것 같다.

- Variadic 매크로 : ...과 __VA_ARGS__
#include <stdio.h>
#include <math.h>
#define PR(X, ...) printf("Message " #X ":"  __VA_ARGS__)
int main(void)
{
   double x = 48.0;
   double y;

   y = sqrt(48.0);  // x변수가 할당되니 컴파일러 오류가 났다. 구문문제는 없는뎅 ㅡㅡa
   PR(1, "x = %g\n", x);
   PR(2, "x = %.2f, y = %.4f\n", x, y);
   return 0;
}
// 결과값
Message 1:x = 48
Message 2:x = 48.00, y = 6.9282

인수의 갯수는 ... 이라는 생략부호가 선언 되어 있다. 인수갯수는 무관하다.
PR(1, ... ) 에서 x는 1의 값을 가지며 따라서 #1은 "1"이 된다. 이숫자는 파라메터의 갯수를 지정하는게 아니므로 어떤 문자가 와도 상관없다. (테스트 다 해봤거덩 ^^;;)

- 매크로 쓸때 조심해라
: 매크로는 문자열 치환이므로 함수와 같은 연산이 일어나지 않으므로 항상 쓸때는 조심해야한다. 어떤 컴파일러는 하나의 라인으로 매크로 정의를 제한하기도 한다. 그렇지 않더라도 하나의 라인을 준수하는게 최선의 방법일 것이다.

매크로대 함수의 선택은 시간과 공간의 맞교환을 나타낸다. 매크로는 인라인 코드를 생성하는데 이건 프로그램 내에서 문장을 갖는다는 의미이다. 매크로를 20번 사용했다면 20개의 코드라인을 갖게 된다. 함ㅅ를 20번 사용하면 함수는 한번만 복사를 하므로 공간을 덜 차지하게 된다.
반면 프로그램 제어는 함수가 있는곳으로 이동해서 호출하고 반환을 한다. 함수는 인라인 코드에 비해 시간이 오래 걸린다. 매크로는 인수 타입에 신경쓰지 않아도 되서 함수보다 쉽게 사용할 수 있는 점이다.

* 매크로 사용시 주의해야 할 사항
1. 매크로 이름에는 공백이 없으나 대체 문자열에서는 나타난다는것을 기억해야한다.
2. 각각의 인수 정의에 전체적으로 괄호를 사용해라. 원하지 않는 결과값이 나올 수 있다.
3. 매크로 함수 이름은 대문자를 사용한다. 가능성있는 부작용을 방지하도록 도와준다.
4. 일반함수와의 속도면에서의 차이가 어느정도 있는지 파악하고 쓰도록해라.
     중첩된 루프의 내부에서의 매크로 함수 사용은 속도를 훨씬 빠르게 향상시킨다.
     많은 시스템은 프로파일러를 제공하여 어느 부분에서 시간이 소비되는지 파악하여 해당
     코드의 속도를 빨리 처리하도록 도와준다.

댓글 없음:

댓글 쓰기