본문 바로가기

BackEnd/C

대표적인 선행처리 명령문

반응형

1. #define : Object-like macro 


앞서 정의한 #define 명령문을 다시 한번 보자.




위 그림에서 보여주듯이 선행처리 명령문은 기본적으로 세 부분으로 나뉘는데,


제일 먼저 등장하는 #define을 가리켜 ‘지시자’라 한다.


선행처리기가 이 부분을 보고 지시하는 바를 파악하기 때문에 지시자라 하는 것이다. 


그리고 앞서 설명했듯이 #define 지시자는 선행처리기에게 다음과 같은 내용을 지시한다.


이어서 등장하는 매크로를 마지막에 등장하는 매크로 몸체로 치환하라.


그리고 #define 지시자 뒤에 등장하는 것을 가리켜 ‘매크로’라 하고, 그 뒤를 이어서 등장하는 것


을 가리켜 ‘매크로 몸체(또는 대체 리스트)’라 한다. 따라서 위의 선행처리 명령문은 다음의 내용


을 선행처리기에게 지시한다. “매크로 PI를 매크로 몸체 3.14로 전부 치환하라!


결과적으로 PI라는 이름의 매크로는 그 자체로 상수 3.14가 된 셈이다.


참고로 PI와 같은 매크로를 가리켜 ‘오브젝트와 유사한 매크로(object-like macro)’라 한다.


오브젝트(object)라는 것은 그 자체로 "완전한 의미를 갖는 대상이나 사물”을 의미한다. 그런데 


위에서 정의한 매크로 PI는 그 자체로 3.14라는 상수를 의미하기 때문에 “오브젝트와 유사한 매


크로” 또는 "오브젝트와 비슷한 매크로”라 하는 것이다. 


다음 예제에서는 다양한 오브젝트 유사 매크로를 보여준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
#define NAME          "홍길동"
#define AGE           24   
#define PRINT_ADDR    puts("주소: 경기도 용인시");
 
int main(void)
{
    printf("이름: %s \n", NAME);
    printf("나이: %d \n", AGE);
    PRINT_ADDR;
 
    return 0;
}

cs


1
2
3
이름: 홍길동
나이: 24
주소: 경기도 용인시
cs


• 3, 4행: 3, 4행과 같이 상수를 몸체로 두고 있는 매크로를 가리켜 ‘매크로 상수’라고도 부른다. 또


한 위 예제에서 정의하고 있는 매크로처럼 매크로는 대문자로 정의하는 것이 일반적이다. 대문자


로 정의함으로 인해서 변수나 함수의 이름과는 쉽게 구분이 되고, 이 식별자가 매크로라는 사실을 


부각시킬 수 있기 때문이다. 


• 5행 : 함수의 호출 문도 매크로로 정의될 수 있다. 뿐만 아니라 원하는 대부분의 것이 매크로로 정의될 수 있다. 


• 9행 : 전처리 과정을 거치면 이 문장은 다음과 같이 변경된다. 매크로 NAME이 문자열 “홍길동”으로 대체된다.  printf("이름: %s \n", "홍길동");


• 10,11행 : 매크로 AGE는 상수 24로, 매크로 PRINT_ADDR은 puts 함수의 호출 문으로 대체된다. 


2. #define : Function-like macro



매크로는 매개변수가 존재하는 형태로도 정의할 수 있다. 그리고 이렇게 매개변수가 존재하는 매

크로는 그 동작방식이 마치 함수와 유사하여 ‘함수와 유사한 매크로(Function-like macro)’라 하

는데,줄여서 “매크로 함수”라고도 부른다. 

다음은 매크로 함수의 예이다. 

#define SQUARE(X) X*X 

#define으로 시작을 하는 것은 오브젝트 유사 매크로와 동일하다. 

그러나 매크로에 괄호가 등장함으로 인해서 아래 그림에서 보여주는 바와 같이 해석이 된다. 

여기서 괄호 안에 존재하는 X는 정해지지 않은 임의의 값(또는 문장)을 의미한다. 

그리고 위의 정의를 접한 선행처리기는 SQUARE(X)와 동일한 패턴을 만나면, 무조건 X * X 로 치

환해버린다. 예를 들어서 아래는

SQUARE(123); 
SQUARE(NUM);

선행처리 후에 다음과 같이 변경이 된다.

123*123; 
NUM*NUM;

함수의 호출과 유사하다. 참고로 이렇게 선행처리기에 의해서 변경이 되는 과정 자체를 가리켜 

‘매크로 확장(macro expansion)’이라 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#define SQUARE(X)   X*X
 
int main(void)
{
    int num=20;
 
    /* 정상 결과 출력 */
    printf("Square of num: %d \n", SQUARE(num));
    printf("Square of  -5: %d \n", SQUARE(-5));
    printf("Square of 2.5: %g \n", SQUARE(2.5));
 
    /* 비정상 결과 출력 */
    printf("Square of 3+2: %d \n", SQUARE(3+2));
 
    return 0;
}

cs


1
2
3
4
Square of num: 400
Square of  -525
Square of 2.56.25
Square of 3+211
cs


위 예제를 통해서 함수 유사 매크로가 처리되는 방식을 확인할 수 있다. 더불어 2행에 정의된 매크


가 잘못 정의된 매크로라는 사실도 알 수 있다. 


3. 잘못된 매크로 정의


위 예제 14행을 함수의 관점에서 본다면 3과 2의 합인 5를 SQUARE 함수의 인자로 전달하는 것


으로 생각하는 것이 당연하다. 하지만 다음과 같이 치환된다.


3+2*3+2 


따라서 11이 출력되는 것은 아주 당연하다.


해결책은 2행 매크로를 다음과 같이 정의하면, 정상적인 값의 출력을 기대할수 있다.


#define SQUARE(X)   (X)*(X)


그러나 여전히 문제는 남아있다. 다음 문장을 예로 들겠다. 


int num = 120/SQUARE(2);


SQUARE(2)는 4이므로 변수 num이 30으로 초기화될 것을 기대할 수 있다. 그런데 실제로 초기


화 되는 값은 120이다. 왜냐하면 다음과 같이 치환이 되기 때문이다. 


int num = 120/(2)*(2);


따라서 이런 저런 문제를 모두 해결하기 위해서는 다음과 같은 형태로 매크로를 정의해야 한다.


#define SQUARE(X)  ((X)*(X)) 


이제 변수 num은 다음 식에 의해서 30으로 초기화된다. 


int num = 120/ ((2) * (2));


이처럼 함수 유사 매크로를 정의할 때에는 매크로의 몸체 부분을 구성하는 X와 같은 전달인자 


하나하나에 괄호를 해야 함은 물론이고,반드시 전체를 괄호로 한번 더 묶어줘야 한다는 사실


을 기억하자.


4.  매크로를 두 줄에 걸쳐서 정의


정의하는 매크로의 길이가 길어지는 경우에는 가독성을 높이기 위해서 두 줄에 걸쳐서 매크로를 


정의하고 싶을 때도 있다.


따라서 다음과 같이 \ 문자를 활용해서 줄이 바뀌었음을 명시해야 한다.


#define SQUARE (X) \

((X)*(X))


5. 매크로 정의 시, 먼저 정의된 매크로도 사용이 가능하다.


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
#define PI 3.14
#define PROUDCT(X, Y)   ((X)*(Y))
#define CIRCLE_AREA(R)  (PROUDCT((R), (R))*PI)
 
int main(void)
{
    double rad=2.1;
    printf("반지름 %g인 원의 넓이: %g \n", rad, CIRCLE_AREA(rad));
 
    return 0;
}

cs


1
반지름 2.1인 원의 넓이: 13.8474
cs


위 예제의 5행에서는 3행과 4행에서 정의한 매크로를 활용해서 매크로 함수를 정의하고 있다. 이


이전에 정의된 매크로는 새로운 매크로를 정의하는데 사용이 가능하다.



6. 매크로 함수의 장점, 단점


1) 장점


• 매크로 함수는 일반 함수에 비해 실행속도가 빠르다. 


• 자료형에 따라서 별도로 함수를 정의하지 않아도 된다.


2) 단점


• 정의하기가 까다롭다.


• 디버깅하기가 어렵다. 


7. 매크로 함수가 되기 위한 조건


앞에서 설명한 매크로 함수의 장점과 단점을 종합해보면,다음의 특성을 지니는 함수들을 매크로


의 형태로 정의하는 것이 좋다는 결론이 나온다.


• 함수의 크기 또는 길이가 짧은 간단한 함수 


• 호출이 되는 빈도수가 높은 함수

 

우선 함수의 크기 또는 길이가 짧아야 매크로의 형태로 정의하기가 편리하고 에러의 발생 확률이 


낮아서 디버깅에 대한 염려를 덜 수 있다. 그리고 호출의 빈도수가 높아야 매크로 함수가 가져다 


주는 성능 향상의 이점도 최대한 누릴 수 있다. 


반응형

'BackEnd > C' 카테고리의 다른 글

구조체의 정의  (0) 2019.03.22
구조체의 필요성  (0) 2019.03.21
전처리기(Preprocessor)와 매크로  (0) 2019.03.21
sprintf & sscanf  (0) 2019.03.21
문자열의 정보 추출 함수들  (0) 2019.03.21