전처리기, Pre-processor(전처리기)
컴파일을 시도할 경우 컴파일이 실행되기 전에 전처리기 명령부터 처리된다.
전처리기는 # 로 시작하고 ; 를 붙이지 않으며 보이지 않게 소스 코드를 변경하며
컴파일러에게 지시를 내릴 수도 있다.
각각은 여기서는 명령어라는 용어를 사용했지만 정확하게는 directive(지시자) 라고 하는 것이 나을 지 모르겠다.
#include <header.h>
가장 흔히 볼 수 있는 전처리기이다. 해당 파일을 찾아서 컴파일러가 그 파일이 마치 현재 컴파일하는
소스 코드에 포함되어 있는 것같이 해준다. <> 는 표준 헤더 파일일 경우에 설정되어 있는 폴더에서
헤더 파일을 찾으며 “” 는 그 외 폴더에서 찾을 수 있는데 최우선으로 현재 프로젝트 폴더에서 찾게 된다.
#define
define 문은 여러 경우에 사용될 수 있는데 일반적으로 문자열 대치에 사용된다.
#define MAX 512
라고 하면 소스 코드에 있는 MAX 라는 문자를 모두 512 로 대치한다.
int Arr[MAX];
는 실제 컴파일러가 컴파일할 때는
int Arr[512];
로 대치될 것이다. 이것은
#define MAX 512
int Arr[MAX];
가
int Arr[512];
로 바뀐다고 생각하면 된다.
대치되는 문자 없이 그냥
#define MAX
로 사용되는 경우도 흔하다. 이것은 MAX 라는 문자열을 정의하지만 대치시키지는 않는다.
이렇게 사용하는 경우는 #ifdef ~ #elif ~ #else ~ #endif 문과 같이 사용되면 상당히 유용하게 사용된다.
#ifdef MAX
cout << “Max is defined!” << endl;
#else
cout << “Max is not defined!” << endl;
#endif
이렇게 사용하면 #define MAX 한 줄로 여러 버전의 실행 파일을 만들 수도 있다.
이런 것을 조건부 컴파일이라고 한다.
마지막 #endif 를 넣는 것도 잊지 말자.
#ifdef MAX
#elif defined(MIN)
#else
#endif
문이나
#if defined(MAX)
#elif defined(MIN)
#else
#endif
문등의 예제를 잘 봐두기 바란다.
¨ 관련된 주요 전처리기 명령어
¨ #ifdef 식별자
¨ #ifndef 식별자
¨ #undef 식별자
¨ #if defined(식별자)
¨ #if !defined(식별자)
¨ #elif
¨ #else
¨ #endif
#ifdef 등으로 식별자를 사용했으면
#undef 식별자
문으로 undef 하는 습관을 기르자. #undef 이후부터 그 식별자는 정의되지 않은 것으로 간주된다.
예제) http://blog.naver.com/xtelite/50018568445
포함감시(Inclusion Guard)
여러 개의 헤더 파일(.h 또는 .hpp)과 구현 파일(.c 또는 .cpp) 들이 있을 경우 헤더 파일이 중복 포함되는
경우가 많다. 특히 C/C++에 입문한 지 얼마 안되는 사람들에게서 자주 나타나는 데 이런 경우 포함감시를
사용한다. 사실 포함감시 기능은 만드는 모든 헤더파일에 적용하도록 하자.
#ifndef FILENAME_H
#define FILENAME_H
// 여기에 헤더 파일의 모든 내용을 넣는다
#endif
이렇게 해 두면 두 번째 포함될 때 #ifndef 가 거짓이 되어 이 헤더 파일이 포함되지 않는다.
즉,
#include “filename.h”
#include “filename.h”
와 같이 하더라도 두 번째 헤더파일은 포함되지 않을 것이다. FILENAME_H 부분은 관례상 파일명을 이용해서
이렇게 명명한다. _FILENAME_H 나 __FILENAME_H__ 등 잘 사용되지 않는 문자열을 파일이름을 사용해서 만드는
것이다.
매크로 함수(Macro function)
매크로 함수도 전처리기로 흔히 사용된다. 다만 디버깅이 힘들다는 단점 때문에 점점 사용되지 않고 있기도 하다.
특히, C++의 경우 엄격한 형 검사를 하게 되는데 매크로 함수를 사용하게 되면 그 기능을 사용할 수 없으니
피해야 한다.
#define CUBE(x) ((x)*(x)*(x))
int x, y;
…
y = CUBE(x);
는
int x, y;
…
y = (x) * (x) * (x);
로 대치될 것이다. 매크로 함수를 사용한다면 ()를 남발하는 습관을 키워야 한다.
왜 그런지는
y = CUBE(3+4);
와 같은 경우에 직접 대치해 보면 알게 될 것이다.
C++ 사용자는 매크로 함수보다는 template이나 inline 함수를 사용해야 한다.
문자열 조작
#define SAY(x) printf(#x)
SAY(Hello, world!);
와 같이 식별자 앞에 # 를 붙이게 되면 자동으로 “x” 와 같이 “”로 둘러 싸 준다.
결과적으로
printf(“Hello, world!”);
로 대치될 것이다.
문자열 결합
## 는 두 개의 문자열을 결합해 준다.
#define Print(x) Print ## x
와 같이 된 경우
Print(One) 을 사용하면 PrintOne 이라는 문자열로 대치되고 Print(Two) 는 PrintTwo 라는 문자열로 대치된다.
잘 사용하면 아주 유용한 기능이 된다.
ASSERT()
대부분의 컴파일러는 ASSERT() 매크로를 가지고 있다. 여기서는 직접 하나 만들어 보자.
#ifndef DEBUG
#define ASSERT(x)
#else
#define ASSERT(x) \
if ( ! (x) ) \
{ \
printf(#x); \
printf(“ is NULL on line %d in file %s”, __LINE__, __FILE__); \
}
#endif
이 코드의 위에
#define DEBUG
한 줄 포함하면
#define ASSERT(x)
로 아무 일도 하지 않고 DEBUG가 정의되지 않으면 그 아래 함수가 정의된다.
즉, 디버그때만 코드가 생성되고 릴리즈시에는 코드가 생성되지 않게 할 수 있는 것이다.
여러 줄이 필요할 때는 \ 가 사용되었다는 것에 유의하자. 또한 내장 매크로인 __LINE__ 이나 __FILE__ 은
다음에 설명한다.
내장 매크로
컴파일 시에 컴파일러가 미리 정의하고 있는 매크로들이 있다.
각각 오른 쪽에 있는 내용으로 대치된다.
__DATE__ : 컴파일하는 날짜
__TIME__ : 컴파일하는 시간
__LINE__ : 현재 컴파일하고 있는 줄 번호
__FILE__ : 현재 컴파일하고 있는 파일의 이름
#error
컴파일러는 이 명령을 만나게 되면 해당 메지시를 출력하고 컴파일을 중지한다.
C++ 컴파일러에서만 동작하게 하는 다음 코드를 참조하자.
#if !defined(__cplusplus)
#error C++ compiler required.
#endif
__cplusplus 는 C++ 컴파일러일 경우에 정의되는 내장 매크로이다.
#pragma
#pragma 는 컴파일러마다 고유하게 사용할 수 있는 명령어이다. 따라서 그 문법은 컴파일러마다
다르고 그 종류도 많다.
예를 들어
#pragma once
같은 경우 위의 포함감시 기능을 컴파일러가 알아서 해 준다. 즉, 한 번 include 된 헤더 파일은 중복해서
포함되지 않도록 컴파일러가 처리해 준다.
'프로그래밍' 카테고리의 다른 글
공용체, 열거형 상수, typedef문 (0) | 2008.04.25 |
---|---|
C언어에서 실수형의 출력 내용이 정확하지 않은 이유 (0) | 2008.04.25 |
조건부 컴파일 (0) | 2008.04.25 |
EOF (0) | 2008.04.25 |
제어문자 (0) | 2008.04.25 |