본문 바로가기
코스웨어/16년 스마트컨트롤러

2016-08-29_조재찬_스터디일지_C언어-선행처리기와 매크로

by 알 수 없는 사용자 2016. 8. 30.
728x90
반응형

선행처리기(Preprocessor)


선행처리 거친 소스파일은 프로그래머가 인지할 수 있는 형태이다.


선행처리기의 일

- 단순 치환


소스

1
2
3
4
5
6
#define	PI	3.14	// 선행처리기에게 명령하는 문장은 #으로 시작하며 한줄마다 처리된다. (끝에 semi colon붙이지 않음)

int main()
{
	num = PI * 3.5;
}


선행처리 후 소스

#define PI 3.14 //지워짐 


1
2
3
4
int main(void)
{
	num  = 3.14 * 3.5;
}



Object-like macro


#define PI 3.14

directive / macro / macro's body


PI를 상수 3.14로 정의 


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

#define  NAME          "홍길동"
#define  AGE            24   
#define  PRINT_ADDR     puts("주소: 경기도 용인시\n");

int main(void)
{
	printf("이름: %s \n", NAME);
	printf("나이: %d \n", AGE);
	PRINT_ADDR;
	return 0;
}


Output:

1
2
3

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


Function-like macro

#define SQUARE (X) X*X


SQUARE(123); 123*123


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#define SQUARE(X)   X*X

int main(void)
{
	int num=20;

	/* 정상적 결과 출력 */
	printf("Square of num: %d \n", SQUARE(num));	//20*20
	printf("Square of  -5: %d \n", SQUARE(-5));	// (-5)*(-5)
	printf("Square of 2.5: %g \n", SQUARE(2.5));	// 2.5*2.5

	/* 비정상적 결과 출력 */
	printf("Square of 3+2: %d \n", SQUARE(3+2));	// (5)*(5)가 아닌 3+2*3+2 = 11
	return 0;
}


Output:

1
2
3
4
Square of num: 400 
Square of  -5: 25 
Square of 2.5: 6.25 
Square of 3+2: 11 


잘못된 매크로 함수 정의의 해결책


#define SQUARE(X) X*X


바로 위 예제에서 SQUARE(3+2)가 기대했던 25가 아닌 값으로 연산되는걸 해결해보자.



해결책1

#define SQUARE(X)    X*X

SQUARE(3+2)을 SQUARE((3+2))으로 바꿈


(3+2)*(3+2)=25, 하지만 매크로라고 하기 무색하게 사용이 불편



해결책2

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


SQUARE(3+2) (3+2)*(3+2)=25

이 경우는 해결, 하지만...


num=120/SQUARE(2) 120 / (2)*(2)

120/4=30을 기대하지만 120/2=60, 60*2=120으로 연산된다.



최상의 해결책!

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

120 / ((2)*(2)) = 30




매크로를 두 줄에 걸쳐서 정의하는 방법


#define SQUARE(X)  \

((X)*(X))


한줄의 끝에 매크로의 정의가 이어짐을 의미하는 backslash추가




먼저 정의된 매크로의 사용

위에서 먼저 정의된 매크로를 아래에서 사용할 수도 있다.

아래는 이를 응용한 예제이다.


1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#define PI 3.14
#define PRODUCT(X, Y)   ((X)*(Y))
#define CIRCLE_AREA(R)  (PRODUCT((R), (R))*PI)

int main(void)
{
	double rad=2.1;
	printf("반지름 %g인 원의 넓이: %g \n", rad, CIRCLE_AREA(rad));
	return 0;
}

// (((2.1)*(2.1))*3.14) = 13.8474

Output:

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



매크로 함수의 장점 (일반함수와 비교)


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


함수 호출을 완성화기 위해선 별도의 메모리 공간이 필요하고

호출된 함수로의 이동 및 반환의 과정을 거쳐야 한다.

(메모리 공간 할당과 해제)


매크로 함수는 선행처리 과정에서 치환이 이뤄지니 그러한 과정이 불필요하며, 따라서 실행속도가 빠른 것.



- 자료형에 의존적이지 않다.


ADD (int n1, int n2)

dAdd(double n1, double n2)

일반함수는 위와 같이 전달되는 인자의 자료형에 의존적이다.


하지만 매크로 함수는 자료형에 따른 별도의 함수 정의가 필요 없다.



매크로 함수의 단점


1. 정의하기가 까다롭다.


일반함수

int DiffABS (int a, int b);

{

if(a>b)

return a-b;

else

return b-a;

}


정의된 매크로 함수

#define DIFF_ABS(x, y) (x)>(y) ? (x)-(y) : (y)-(x);


이는 그나마 정의하기 쉬운 편이고, 함수의 내용이 복잡할 수록 매크로 함수로는 정의하기가 어려워진다.



2. 디버깅하기가 쉽지 않다.

컴파일러는 선행처리된 이후의 내용(결과물)을 기준으로 오류를 뱉는다. 

따라서 프로그래머 입장에서 오류를 찾기 힘들게 되고 더 신경을 써야할 부분이 생기게 된다.


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#define  DIFF_ABS(X, Y)     ( (x)>(y) ? (x)-(y) : (y)-(x) )

int main(void)
{
	printf("두 값의 차: %d \n", DIFF_ABS(5, 7));
	printf("두 값의 차: %g \n", DIFF_ABS(1.8, -1.4));
	return 0;
}

선언되지 않은 식별자 오류 (매크로 함수의 X, Y 대소문자 구분이 틀림)

Output:

1
2
3
4
5
In function 'main':
Line 6: error: 'x' undeclared (first use in this function)
Line 6: error: (Each undeclared identifier is reported only once
Line 6: error: for each function it appears in.)
Line 6: error: 'y' undeclared (first use in this function)



어떠한 함수를 매크로로 정의하는 것이 좋은가?


작은 크기의 함수

한 두줄 정도 크기의 작은 함수

if~else, for과 같이 실행 흐름을 컨트롤 하는 문장은 배제하는 것이 좋다.



호출 빈도수가 높은 함수

함수를 매크로로 정의하는 가장 큰 이유는 성능적 측면이 크다.

다만, 빈도수의 높은 기준이 어느정도인지는 프로그래머의 주관에 따라 다를 수 있다.


728x90