1. 매크로 기본
C언어에서 매크로는 컴파일러가 컴파일을 하기 전에 전처리기에 의해 처리된다. #으로 시작하며 #define, #include 등이 있다. 다음은 주로 많이 사용하는 매크로들이다. |
Macro |
Description |
#define, #undef |
매크로 상수나 매크로 함수를 정의(해제) 할 때 사용 |
#include |
파일포함 |
#ifdef,#ifndef,
#elif,#else,#endif |
조건부 컴파일 |
#pragma |
컴파일러나 Linker에게 지시할 때 사용 |
#line |
컴파일할 때 출력하는 Line을 변경한다. |
#error |
에러를 출력하고 컴파일러를 중지시킨다. |
2. 매크로 상수와 매크로 함수.
#define 문을 사용하면 매크로 함수와 매크로 상수를 만들수 있다. |
#include <stdio.h>
#define MAX 10
#define SQUARE(x) (x)*(x)
void main()
{
int ar[MAX]; // int ar[10]
int s = SQUARE(5); // int s = (5)*(5)
printf("s = %d\n", s);
} |
매크로 함수는 잘못 사용할 경우 버그를 유발할 수 있으므로 조심해야한다. |
#include <stdio.h>
#define SQUARE(x) (x)*(x)
void main()
{
int x = 3;
int s = SQUARE(++x); // (++x)*(++x)
printf("s = %d\n", s); // s = 25
} |
위의 경우 프로그래머는 16을 예상하지만 결국 x는 2번증가 되어 25가 나올 것이다.
매크로 함수는 모든 형에 적용할 때 사용하는 함수를 만들수 있어 유용하다. |
#include <stdio.h>
#define max(a,b) ( (a) > (b) ) ? (a):(b) // 1
//int max(int a, int b) { return a>b ? a:b;} // 2
void main()
{
printf("%d\n", max(5,3) );
printf("%g\n", max(5.4,3.2) );
} |
2번은 int 이외의 값이 전달될 경우, 엉뚱한 결과를 출력할 것이다. 하지만 1번은 모든 type에 대해 제대로 동작할 것이다. (물론 ++ 연산자를 포함한 식이 전달될 경우는 엉뚱한 결과가 나올 것이다.)
매크로의 정의가 2줄이상을 경우는 '\'기호를 사용해서 연결해야 한다. |
#include <stdio.h>
#define assert(x) if (!(x) ) \
{ \
printf("assert failed. %s(%d)\n", __FILE__, __LINE__); \
}
void main()
{
int n;
scanf("%d",&n);
assert(n);
} |
3. #, ##을 이용한 매크로 정의
#을 사용하면 해당 심볼을 문자형태로 출력할 수 있다. |
#include <stdio.h>
#define dprint(expr) printf(#expr " = %g\n", expr )
void main()
{
double x = 3.4;
double y = 2.0;
dprint(x/y); // printf("x/y" " = %g\n", expr)
} |
위의 예에서 dprint #은 x/y을 "x/y"형의 문자열로 바꾸어 주는 역할을 한다.
##은 2개의 Token을 연결하는데 사용한다. |
#include <stdio.h>
#define tprint(expr) printf("%d, %d\n", expr##1, expr##2)
void main()
{
int a1 = 10, a2 = 20;
tprint(a);
} |
##앞의 symbol과 ##뒤의 symbol이 여백없이 연결이 된다. |
4. 조건부 컴파일 기능
#ifdef, #ifndef, #if, #elif, #else, #endif 등은 조건부 컴파일 기능에 사용된다. 아래 코드를 실행해 보자. |
#include <stdio.h>
#define DEBUG // 주석처리했을 때와 그렇지 않을 때를 비교해 보자.
void main()
{
#ifdef DEBUG
printf("Debug Message\n");
#endif
printf("Good-Bye! ^^\n");
} |
위 코드는 아래와 같은 방법으로도 사용될 수 있다. |
#include <stdio.h>
#define DEBUG
#ifdef DEBUG
#define TRACE(x) printf(x)
#else
#define TRACE(x)
#endif
void main()
{
TRACE("Debug Message\n");
printf("Good-Bye! ^^\n");
} |
또한 아래와 같이 System에 따라 다른 헤더를 포함시킬때도 사용할 수 있다. |
#if SYSTEM == WINNT
#define HDR "winnt.h"
#elif SYSTEM == LINUX
#define HDR "linux.h"
#elif SYSTEM == MAC
#define HDR "mac.h"
#endif
#include HDR |
5. Conditional Inclusion
프로그램을 만들때 여러개의 헤더와 소스 파일을 사용하다 보면 특정 헤더파일이 2번 include되는 경우가 발생할 수 있다. 이 경우 컴파일시에 문제가 발생할 수 있다. |
/* hdr.h */
struct pepople
{
char name[256];
int age;
}; |
/* xxx.c */
#include "hdr.h"
#include "hdr.h"
void main()
{
} |
위의 경우 강제로 2번 include 했지만 실제 작업중에는 피치 못하게 2번 include하게 되는 일이 발생할 수 있다. 이때 헤더 파일이 1번만 include되게 하려면 다음과 같이 하면된다. |
/* hdr.h */
#if !defined(HDR) // #ifndef HDR 과 동일
#define HDR
struct pepople
{
char name[256];
int age;
};
#endif /* HDR */ |
/* xxx.c */
#include "hdr.h"
#include "hdr.h"
void main()
{
} |
모든 헤더파일에는 위와 같은 매크로를 넣어주는 것이 일반적이다. |
6. 미리 정의된 매크로
C 에는 사용자가 정의 하지 않더라도 미리정의되어 있는 매크로 상수가 몇가지 있다. |
Predefine Macro |
Description |
__FILE__ |
현재 컴파일되는 파일의 이름 |
__LINE__ |
현재 컴파일되는 소스의 Line No. |
__DATE__ |
현재 날짜 |
__STDC__ |
현재 컴파일러가 ANSI 표준을 따를 경우 1로 정의 된다. |
__TIME__ |
시간 |
__TIMESTAMP__ |
|
#include <stdio.h>
void main()
{
printf("%s\n", __FILE__);
printf("%d\n", __LINE__);
printf("%s\n", __DATE__);
printf("%s\n", __TIME__);
printf("%d\n", __TIMESTAMP__);
} |
7. #pragma
#pragma 매크로는 컴파일러나 Linker에게 어떤 지시를 하기 위해 사용한다.
#pragma 지시어
형식으로 사용된다. 지시어의 종류는 컴파일러 다르지만 vc++에서 사용되는 지시어는 대략 아래와 같은 것들이 있다. |
alloc_text |
auto_inline |
bss_seg |
check_stack |
code_seg |
comment |
component |
conform |
const_seg |
data_seg |
deprecated |
function |
hdrstop |
include_alias |
init_seg1 |
inline_depth |
inline_recursion |
intrinsic |
managed |
message |
once |
optimize |
pack |
pointers_to_members1 |
pop_macro |
push_macro |
runtime_checks |
section |
setlocale |
unmanaged |
vtordisp1 |
warning |
message 라는 지시어를 사용하면 컴파일중 Output 창에 메시지를 출력하는 것이 가능하다. |
void main()
{
#pragma message("이 메세지 컴파일러가 컴파일하는 중에 출력창에 출력됩니다.")
} |
# 매크로와 #pragma, __FILE__, __LINE__ 등을 응용하면 아래와 같이 유용한 매크로를 정의해서 사용할 수 있다. |
#define TOSTR(x) #x
#define STR(x) TOSTR(x)
#define MSG(desc) message(__FILE__"("STR(__LINE__)"):"#desc)
void main()
{
int x = 10;
int y = 20;
#pragma MSG(이 코드는 나중에 다시 살펴볼 필요가 있습니다) // A
x = y / 2;
......;
......;
} |
위의 A처럼 코드를 만들어 둘 경우 매번 컴파일 할 때마다 메시지를 볼수 있을 것이다.
프로그램이 길어지고 나중에 변경할 필요가 있을 때 잊지 않고 작업을 할 수 있을 것이다. |
8. #line, #error
#line매크로는 컴파일러가 출력하는 현재 line count를 변경하는 매크로 이다. 자주 사용되지는 않는다. |
void main()
{
#line 100
int x = 10 // error ;를 빼 먹었다.
} |
위 코드를 컴파일 하면 ;이 빠져서 에러가 날것이다. 그런데 101번째 line이라고 나오게 될것이다.
#error 매크로는 컴파일을 멈추고 에러를 출력하게 하는 매크로 이다. |
// #define LINUX
void main()
{
#ifndef LINUX
#error this os is not linux
#endif
} | |