728x90
반응형
컴파일을 시도할 경우 컴파일이 실행되기 전에 전처리기 명령부터 처리된다.
전처리기는 # 로 시작하고 ; 를 붙이지 않으며 보이지 않게 소스 코드를 변경하며
컴파일러에게 지시를 내릴 수도 있다.
각각은 여기서는 명령어라는 용어를 사용했지만 정확하게는
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 된 헤더 파일은 중복해서 포함되지 않도록
컴파일러가 처리해 준다.
매크로 함수 정의시 주의할 점
① 매크로 함수에 사용되는 모든 인자에는 반드시 괄호를 해두는
것이 안전한 정의방법이다. 예를 들어 다음의 매크로 함수를 보자.
이 경우 괄호를 사용하지 않고 정의한 것으로, 부작용이 발생한 것이다.
위 매크로 함수의 전개 후를 보면 알겠지만, 결과는 예상했던 21이 아니라
11이 나온다. 이러한 부작용을 방지하기 위해 가능하면 모든 인자에
괄호를 해두는 것이 좋다.
② 매크로 함수 내에서는 단일 연산자인 증가 연산자(++) 또는 감소
연산자를 사용하지 않는 것이 좋다. 다음의 예를 보자.
#define SQR(x) ((x)*(x))
void main()
{
int i=2;
printf("%d\n", SQR(++i));}
이렇게 매크로 함수 SQR()을 정의하고, 매개변수로 ++i 를
주었을 때 전개 결과는 다음과 같다.
printf("%d\n", (++i)*(++i));
이를 보면 알겠지만 증가 연산자가 한 문장 안에서 동시에 쓰였다.
원하는 내용은 i 가 1증가하는 것이지만 실제로는 2가 증가하게 된 것이다.
실제로 결과값은 3*4==12 가 나온다.
매크로 함수를 사용하는 이유
- 매크로 함수를 사용하는 이유는 간단한 함수를 사용할 때
굳이 실제의 함수를 만들지 않고, 매크로 함수로 만들어 사용함으로써
보다 간단한 프로그래밍을 할 수 있기 때문이다. 물론 복잡한 기능을 가지는
함수들은 매크로 함수로 만들 수 없겠지만, 간단한 연산을 하는 정도의 함수는
매크로 함수로 작성하는 것이 좋다. 가령 간단한 연산을 하는 함수가 굉장히
빈번하게 프로그램 곳곳에서 호출된다고 생각해 보자. 함수가 호출될 때
생기는 여러 가지 작업들(예를 들어 자동변수를 만들었다 없애는 등의 작업)
때문에 상당히 많은 시간을 소비하게 된다. 특히 반복문 안에서 그 함수를 호출하게 되면,
그 시간은 더욱 길어진다. 하지만 이런 함수를 매크로 함수로 바꾸게 되면,
실제적으로 함수를 호출하는 일이 없어서 빠른 시간에 작업을 처리할 수 있게 된다.
물론 단점도 있는데, 함수는 하나의 루틴을 계속 불러서 사용하지만 매크로
함수는 모두 치환이 되기 때문에 실행 파일의 크기가 커질 수도 있다.
D) 매크로 함수의 용도
① 중복되는 수식을 매크로 함수로 간결하게 기술
- 프로그래밍을 하다보면 특정 수식이 계속해서 쓰이는 경우가 있는데,
이것을 따로 함수로 만들어 쓰자니 실행속도가 문제될 것 같고, 그대로
수식으로 쓰자니 프로그램이 너무 복잡해 질 것 같은 상황에서,
이러한 수식을 매크로 함수로 치환하면 소스가 판독하기 쉽고 간결해진다.
② 표준 라이브러리 함수의 특정 실매개변수를 특정 값으로 지정한 경우
- 프로그램을 작성하다 보면 표준 라이브러리 함수의 특정한 매개변수를
항상 특정한 값으로 지정하여 쓰는 경우가 많다. 예를 들어 HGC에서는
floodfill() 함수의 3번째 매개변수가 항상 1일 수 밖에 없다. 이처럼
floodfill(x, y, 1) 과 같이 매번 1이라는 값을 지정해 주기가 번거러울
때 매크로 함수를 쓰면 된다.
#define paint(x, y) floodfill(x, y, 1)>③ 매개변수의 데이터형을 제한하고 싶지 않을 때
- 실제함수는 실매개변수의 데이터형이 제한되어 있지만, 매크로 함수는
데이터형이 제한되어 있지 않으므로 모든 데이터형을 매개변수로
사용할 수 있다.
728x90
'기술자료 > C C++' 카테고리의 다른 글
[賢彬] 변환 연산자들(Conversion Operators) (0) | 2009.08.12 |
---|---|
[賢彬] [무료제공 기술서적] Inside C#pdf 와 Source File (마이크로소프트 무료 배포판) (2) | 2009.08.12 |
[賢彬] C Pointer and Arrays (0) | 2009.08.12 |
[오락실]cout 으로 양식화된 출력 사용하기 (0) | 2009.08.12 |
const에 애하여............ (1) | 2009.08.11 |
VISUAL C/C++ PROJECT SETTING (1) | 2009.08.11 |
[賢彬] _STDC_, _P() 의 의미. (1) | 2009.08.11 |
[賢彬] C++ 에서 멤버 함수포인터 사용하기 (1) | 2009.08.10 |