본문 바로가기
스터디/C++

[ C++ ] 선행처리기 / 전처리기 ( Preprocess )

by 알 수 없는 사용자 2019. 9. 5.

다음 포스터인 inline에 들어가기 전에 C에서 배웠던 전처리기에 대해 복습하자.

 

 

 

C++에서 컴파일되는 과정은 다음과 같습니다.

 

"프로그래머가 작성한 소스파일은 가장 먼저 소스파일에 대한 처리를 하는 선행 처리기를 거치고, 컴파일되어 오브젝트 파일이 생성되고, 다시 링커에 의해 실행파일이 된다. "

 

 

즉, 전처리이자 선행 처리(Preprocess) 란?

 

실행 파일을 생성하는 과정에서

컴파일하기 전, 소스 파일 내에 존재하는 선행 처리 지시문을 처리하는 작업을 의미합니다.

 

또한, 선행 처리를 위한 명령에는#define,, #undef, #include, #if, #ifdef, #elif, #else 등

맨 앞에 #기호가 붙는다는 공통점이 있습니다.

 

선행처리를 위한 명령 = 전처리 문 = 전처리기 연산자 )

 

 

#include

특정 프로그램 파일을 현재 위치에 첨부하여, 하나의 파일처럼 사용합니다.

여러 개의 파일을 프로젝트에 등록하면, 서로 설정된 함수나 변수를 사용할 수 있습니다.

 

C언어에서는 입출력을 위한 printf 함수와 scanf 함수의 호출을 목적으로

헤더 파일인 <studio.h>를 포함하기 위해서 다음과 같이 선언했다.

#include <stdio.h>

 

C++에서는 입출력 관련된 함수를 가져올 때 다음과 같이 선언했다.

#include <iostream>

여기서 의문점이 들 수도 있다.

"C++의 헤더 파일에는 확장자가 없나?"

 

그게 아니라 C++에서 표준 헤더 파일의 선언은 확장자를 생략하기로 약속되어 있기 때문이다.

 

#define

상수를 이름으로 정의하거나 매크로 함수를 정의할 때 사용하는 전처리 문이다.

#define을 잘 활용하면 가독성을 높일 수 있다.

 

#define으로 상수를 선언하는 것을 매크로라고 부르는데, 선언하는 방법은 다음과 같이 정의할 수 있다.

#define PI 3.141592 

그리고 실제 사용할 때는 PI라는 이름으로 사용하면 된다.

 

C++ #define

 

즉, 선행 처리자가 #define문을 인식하고 PI 부분은 3.141592라는 상수 값으로 치환한다.

 

그에 대한 단점으로 치환으로 인해 코드 크기가 커지고

치환한 후에 컴파일을 하기 때문에 어디서 에러가 발생했는지 모르는 혼란을 야기할 수도 있다.

 

 

#define을 이용하여 상수뿐만 아니라 함수도 다음과 같이 선언할 수 있다.

#define SQUARE(x) ((x)*(x))

 

이를 매크로 함수라고 하는데, 그럼 실제 사용할 때는 SQUARE( value ) 해서 사용하면 된다.

 

C++ #define

 

의문점이 들 수도 있다.

 

"그렇다면 함수와 매크로 함수 정의 차이가 무엇일까?"

 

C언어에서 소스코드 중복을 줄이고 코드의 재사용성을 높이기 위해서 함수라는 개념을 제공해줬으나,

함수를 호출할 때 스택 프레임이 일어나기 때문에 속도가 느려지는 단점이 있습니다.

 

따라서 코딩할 때는 함수처럼 구성하면서 실제 기계어로 번역될 때는 직접 코딩한 것처럼 처리해주는 매크로 함수 기법을 제공하는 겁니다.

 

정리해보면

함수실행파일의 크기를 줄여주고 코드의 재사용성을 높여주지만 실행 속도가 늦어지는 단점이 있다.

매크로 함수 실행파일의 크기가 커지고 스택 프레임을 사용할 수 없기 때문에 재귀 호출 구조를 만들 수 없다는 단점은 있지만 실행 속도가 증가하는 장점이 있다.

 

#undef 

#undef는 #define와 반대라고 생각하시면 됩니다. 

#define으로 정의한 내용을 #undef를 사용하면 정의된 것이 없었던 일로 됩니다.

#define WIDTH 80
#define ADD( X, Y ) ((X) + (Y))
.
.
.
#undef WIDTH
#undef ADD

 

#if, #elif, #else, #endif, #ifdef, #ifndef

다음은 조건부 컴파일을 위한 전처리기 연산자이다.

 

여기서 조건부 컴파일은 미리 정의된 상수를 통하여 특정 조건에 따라 알맞은 소스코드만을 컴파일 하기 위한 기법이다.

조건부 컴파일 전처리기 연산자는 조건문인 if, else와 사용방법이 매우 유사합니다.

 

#if 조건1
	코드1
#elif 조건2
	코드2
#else
	코드3
#endif

이와 같은 형태로 사용됩니다.

 

조건문과 비교하자면

if 대신에 #if, #ifdef, #ifndef

else if 대신에 #elif

else 대신에 #else

조건부 컴파일 구문이 끝나는 곳에선 #endif를 붙입니다.

 

#if defined(A)는 #ifdef A와 같다고 보시면 됩니다. (A 매크로가 있는 경우)

 

예로 들어, AAA라는 상수가 정의되어있는지 아닌지 판단하는 경우를 살펴보자.

 

 

AAA가 정의되어 있는 경우

 

AAA가 정의되어 있지 않은 경우

 

첫 번째의 경우 #define 되어있어서 "AAA는 정의되어있다."라고 출력이 되었고,

두 번째의 경우 #define 된 AAA가 #undef에 의해 정의된 것이 사라져서, "AAA는 정의되어 있지 않다."가 출력된 것을 볼 수 있다.

 

그래서 첫 번째의 경우에는 조건이 성립하므로 #else문에 있는 12행의 내용은 컴파일에서 제외됩니다.

그러나 두 번째의 경우에는 조건이 성립하지 않으므로 #if문에 있는 12행의 내용이 컴파일에서 제외됩니다.