본문 바로가기
프로그래밍/C언어.

#define 사용법, #define에서 # 사용법. 예제소스&그림

by K_Coder 2012. 2. 26.


define란? #define 사용법. 간단 예제.

매크로와 전처리기 지시자.

#define에서  #의미, # 사용법. 



다룰 내용

매크로 상수와 매크로 함수.
 
#define 이 필요한 간단한 이유.

#define 사용시 반드시 주의해야 할 점.

#define 에서의 #의 의미와 사용법.



#define PI 3.14 라고 가정하면

#define를 전처리기 지시자 라고 하며, PI를 매크로 상수 라고 한다.

아래 소스를 살펴보자. 
  
#include <stdio.h>
#define PI 3.14

int main()
{
	double tmp;

	printf("PI = %.2lf\n", PI);

	tmp = PI + 1;

	printf("tmp = %.2lf\n", tmp);

//	PI = 3; or PI += 1  or PI++  Error
	return 0;
}



이를 PI라는 변수를 만들어서 3.14를 넣은것과 같다고 착각하면 안된다.
결과는 비슷해 보일 수 있으나, 엄밀히 말하면 다른 것이다.

define은 단순한 치환으로써.

내가 7을 "행운의 순자" 라고 부른다 한다면. [ #define 행운의숫자 7 ]
[ 행운의숫자 - 1 ] 라고 쓰여 있다면 ?

당연히 7 - 1 로 생각해서 6이라 답하겠지

행운의숫자 라는게 문자임에는 틀림없으나. 실제로는 7 이라는 숫자를 대신하고 있는 것.
일종의 별명과 같은 개념이다. 내 이름과 별명이 있지만. 뭐라고 부르던 알 수 있듯이.

따라서 PI 또한 3.14 라는 숫자를 대신하고 있는 것. 즉 상수 이다.

 때문에 PI의 값은 변경이 불가능 하며, [ 7 = 3;  말이 되겠는가? ]
printf 로 찍어도 주소값이 나오며 여전히 상수이다.

이렇게 상수에 이름을 붙여주는 것을 "매크로 상수" 라고 한다. 

간단하게는 컴파일 후에는 아래 주석처럼 "치환" 된다고 이해해도 될 것이다.

#include <stdio.h> #define PI 3.14 int main() { double tmp; printf("PI = %.2lf\n", PI); // printf("PI = %.2lf\n", 3.14); tmp = PI + 1; // tmp = 3.14 + 1; printf("tmp = %.2lf\n", tmp); // PI = 3; or PI += 1 or PI++ Error return 0; }



또한, 이런 상수 뿐 아니라 함수로도 사용이 가능하다.

이를 "매크로 함수" 라고 하며 아래의 코드를 살펴 보자.

#include <stdio.h>
#define SWAP(x,y) { int t; t = x; x = y; y = t;}

int main()
{
	int a = 5, b = 10;
	printf("a = %d\t b = %d\n", a,b);

	SWAP(a,b);

	printf("a = %d\t b = %d\n", a,b);
}

 



실제로 동작하는 과장을 설명하자면
SWAP(a,b) 자리에 아래처럼 코드가 치환되서 들어가는 것이다.
#include <stdio.h>
#define SWAP(x,y) { int t; t = x; x = y; y = t;}

int main()
{
	int a = 5, b = 10;
	printf("a = %d\t b = %d\n", a,b);
	
	{ 
		int t;
		t = a;
		a = b;
		b = t;
	}

	printf("a = %d\t b = %d\n", a,b);
	return 0;
}


당연히 동일한 결과가 나온다.
다만 SWAP(a,b) 라고 했기 때문에 x,y 자리가 a,b로 변해서 들어간다.


이런식으로 간단한 함수 등으 정의하여 매크로 처럼 편히 사용할 수 있다.

#include <stdio.h>
#define KCODER(x) x * x

int main()
{
	int res, num = 4;

	res = KCODER(num);	
//	res = num * num			이렇게 되겠죠?

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

	return 0;
}



여기서 보자면 res = KCODER(num); 가
res = num * num ; 로 바뀌게 되는 것이다.

단순히 치환이기 때문에 자료형에는 구애 받지 않으며,
문자 자체가 바뀌는 것이랑 float인지 int 인지 전혀 상관이 없다.

찾아 바꾸기로 바꾼 것과 같다고 생각하면 편할 것이다.

만약, 제곱을 구하는 것을 함수로 구현한다면
 정수인 경우와 실수인 경우의 함수를 따로 만들어야 했다. 



여기까지 왔다면 이제 이게 왜 필요한가? 생각을 해보자

내가 3.14 라는 값이 필요한데, 나중에 이 값이 4.14로 바뀌었다?
그럼 코드에 모든 3.14를 찾아서 바꿔줘야 하는 번거로움이 있다

물론, 찾아바꾸기로 한번에 바꾸면 그만이다.

하지만, 그래도 문제는 있다. 값 중에서
3.14527 라는 값이 있었다면? 4.14527 로 변경되겠지.

내가 바꾸고 싶은 것은 3.14 라는 값 뿐인데...

심지어. 1273.14572 이라는 값 마져도.
127 3.14 527 = > 1274.14527로 바뀌지 않겠는가?

여러가지 조건으로 주어서 나름 바꿀 수도 있겠지만 #define 으로 해두면
#defien PI 3.14 => #define PI 4.14 로 한글자만 바꾸면 된다.

그럼. #define 단순히 글자를 대체 해주는 기능과 마찬가지인 것을 깨달았다면.

아래 코드를 한번 살펴보자.

#include <stdio.h>
#define KPRINT(x) printf("kcoder = %d\n", x);

int main()
{
	int kcoder = 5;

	KPRINT( kcoder );

	return 0;
}


간단하고 동일한 출력에서 매번 printf() 라고 쓰기 귀찮다면
이러한 방법도 가능하다.

물론, 예를 설명을 한 것으로 굉장히 멍청한 코드이다.
printf 를 쓰는 것 보다 KCODER 를 쓰는게 더 힘들뿐더러 남들이 알아보기 힘들테니까.

누구나 한번에 알아볼 수 있는 매크로 함수명으로
잘 만들어서 사용한다면 요긴하게 사용할 수도 있긴 하겠지만.

무튼, 위의 코드는 당연히 문제 없는 코드지.



이제 주의 점을 알아보자.
 

어떤 결과가 나올지 미리 손으로 계산해 보라.

#include <stdio.h>
#define KCODER(x) x * x
#define P(x) printf("%d\n", x);

int main()
{
	int num = 5;

	P( KCODER(num+2) );

	P( 100 / KCODER(2) );

	return 0;
}


정답은 
.

.

.

.

.

.




계산한 값과 동일하게 나왔는가?

일반적으로 틀리게 나왔을 것이다. 이유가 뭘까?

위에서 계속 강조했듯 문자의 단순 치환이라 생기는 문제로
아래 소스를 보면 그나마 조금 이해가 될 것이다.

#include <stdio.h>
#define KCODER(x) x * x
#define P(x) printf("%d\n", x);

int main()
{
	int num = 5;

	P( KCODER(num+2) );
//	printf("%d\n", num+2 * num+2)
//	printf("%d\n", 5+2 * 5+2)

	P( 100 / KCODER(2) );
//	printf("%d\n", 100 / 2 * 2)

	return 0;
}


KCODER(num+2) 는 4+2가 되고 6이 넘어갈 것 같지만
계속 이야기 하듯이 단순히 글자만 바꿔주는 것이기 때문에 계산해서 주지 않는다.
그냥 그 글자들 그대로, 바뀌는 것이다.

11 라인에서 보이듯이 최종적으로는 5+2 * 5+2 로 바뀌진다.

누구나 알고 있다. 곱하기와 더하기 중 어느 연산을 먼저 해야 하는지..
따라서 5 + 2*5 + 2 가 되서 5 + 10 + 2 로 17이 되는 것.

그렇기 때문에 이렇게 사용하고 싶다면   괄호를 굉장히 잘 사용해 주어야 한다.

KCODER(x) (x) * (x) 로 해주어야 정상적인 값이 나온다.

KCODER(num+2) => (num+2) * (num+2) 가 되므로


이정도 보면 이제 문자에 의한 단순 치환이란 의미가 이해가 될 것이다.
그래서 연산이 있는 경우 최대한 괄호를 정확하게, 많이 해줘야 한다.

되도록이면 당연한 계산이라도 괄호를 해주는 것이 안전하다.



#defind에서의 # 에 쓰임에 대해서 알아보자.

마지막. 아래 코드의 결과를 예상해 보자. ( #x 오타 아님.)

#include <stdio.h>
#define KCODER(x) x * x
#define P(x) printf(#x " = %d\n", x);

int main()
{
	int a = 5, b = 3;

	P( a + b );
	P( a * 3 );
	P( b / 5 );
	P( (100+a)*2 );

	return 0;
}


일단 결과를 미리 보도록 합니다.



코드를 보면 a+b, a*3, b/5, (100+a)*2 를 출력하라고 작성한 부분이 보이지 않는다.

 

눈치가 빠른 분들이라면 알 수도 있지만..
P(x) 에서 x의 내용이 계산되지 않고 문자 그대로 출력 되었다.

printf( #x " = %d\n", x);  짐작을 해보자면
#x 가 가장 유력할 것이다. 뒷 부분은 아는 내용이니까.

이것을 이해하기 위해선 2가지를 생각해야 한다.

첫번째. #x 말고 알고 있는 뒷부분만 생각해 보자.

printf( #x " = %d\n", a+b);
알다싶이 x가 a+b로 변경되었다..

그럼, printf(" x  = %d\n", x); 이렇게 했다면?

만약에 이렇게 해서 된다고 가정해볼까?

#define P(x) printf("text => %d\n", x)
어떻게 출력이 되겠는가?

tea+bt => 8 

진정 이것을 원하는가? 이러면 찾아바꾸기 잘못하는 거랑 뭐가 다른가..
그렇기 때문에 " " 안에 있는 문자는 치환의 해당사항이 아니다.

두번째 #을 붙인 경우 x 가 문자열로 대체가 된다는 것.

"x" 이렇게 되는 것이죠. 즉,   "num+2" * "num+2" 

문자들끼리 곱하기라니.. 이런 경우는 당연히 계산도 안되겠죠.

하지만 다른 상황이 있다.

바로, " " 과 " " 이 연산 없이 그냥 붙어있는 경우.

#x 위치에 "a+b"가 들어가서 printf( "a+b" " = %d\n", a+b);

"a+b"   " = %d\n" 

 문자열과 문자열. 즉, " " 과  " " 이 바로 붙는 경우
하나의 문자열로 인식합니다.

결론적으로 "a+b = %d\n" 가 되는 것이죠.


이와 같은 방법을 사용한다면, 출력 시 마다 직접 써주지 않고,
한번에 출력 대상과 연산 결과 값을 출력 할 수 있게 된다.