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
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 -5: 25 Square of 2.5: 6.25 Square of 3+2: 11 | 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 |