선행처리기
선행처리(preprocess)란?
선행처리란 실행 파일을 생성하는 과정에서 소스 파일 내에 존재하는 선행처리 지시문을 처리하는 작업을 의미합니다.
이러한 선행처리 작업은 컴파일하기 전 선행처리기(preprocessor)에 의해 먼저 처리됩니다.
선행처리기는 코드를 생성하는 것이 아니라, 컴파일러가 컴파일하기 좋도록 소스를 재구성해 주는 역할만을 합니다.
C언어의 실행 파일 생성 순서에 대한 더 자세한 사항은 C언어 C 프로그래밍 수업에서 확인할 수 있습니다.
선행처리문의 특징
1. 선행처리문은 선행처리 문자(#)로 시작합니다.
2. 선행처리문은 코드 내에서 하나의 라인을 모두 차지하며, 선행처리문 뒤에 C언어 코드를 추가하여 같이 사용할 수 없습니다.
3. 선행처리문은 다른 C언어의 명령문과는 달리 맨 뒤에 세미콜론(;)을 붙이지 않습니다.
4. 선행처리문은 소스 파일 어디에나 위치할 수 있지만, 선행처리문이 위치한 곳에서부터 파일의 끝까지만 영향을 미칩니다.
선행처리 지시자(preprocessing directives)
선행처리 문자(#)로 시작하는 선행처리 지시자는 다음과 같습니다.
선행처리 지시자 | 설명 |
---|---|
#include | 외부에 선언된 함수나 상수 등을 사용하기 위해, 함수나 상수가 포함된 외부 파일을 현재 파일에 포함할 때 사용함. |
#define | 함수나 상수를 단순화해주는 매크로를 정의할 때 사용함. |
#undef | #define 지시자로 이미 정의된 매크로를 삭제할 때 사용함. |
#line | __LINE__ 매크로와 __FILE__ 매크로를 재정의할 때 사용함. |
#error | 지정한 오류 메시지를 출력하고, 컴파일 과정을 중단하고자 할 때 사용함. |
#pragma | 프로그램의 이식성을 위해 운영체제별로 달라지는 지시사항을 컴파일러에 전달할 때 사용함. |
#if, #ifdef, #ifndef, #elif, #else, #endif |
조건부 컴파일 지시자 |
#include
#include 선행처리 지시자는 외부에 선언된 함수나 상수 등을 사용하기 위해서 헤더 파일을 현재 파일에 포함할 때 사용합니다.
선행처리기는 #include 지시자 뒤에 나오는 파일 이름을 보고 해당 파일을 찾아서 그 내용을 현재 파일에 포함해 줍니다.
#include 선행처리 지시문에서 파일 이름을 표시하는 방법에는 다음과 같이 두 가지 방법이 있습니다.
1. #include <stdio.h>
2. #include "myStdio.h"
C언어에서 제공하는 표준 헤더 파일을 포함할 때에는 보통 꺾쇠괄호(<>)를 사용합니다.
꺾쇠괄호를 사용하여 파일 이름을 표시하면, 선행처리기는 가장 먼저 표준 시스템 디렉터리에서 파일 이름에 해당하는 헤더 파일을 찾습니다.
하지만 표준 시스템 디렉터리에서 파일 이름에 해당하는 헤더 파일을 찾지 못하면, 현재 작업 디렉터리도 검색합니다.
사용자가 직접 작성한 헤더 파일을 포함할 때에는 보통 큰따옴표("")를 사용합니다.
큰따옴표를 사용하여 파일 이름을 표시하면, 선행처리기는 가장 먼저 현재 작업 디렉터리에서 파일 이름에 해당하는 헤더 파일을 찾습니다.
하지만 파일 이름에 해당하는 헤더 파일을 찾지 못하면, 표준 시스템 디렉터리도 검색합니다.
따라서 결과적으로 이 두 방법에 큰 차이는 없지만, 많은 개발자가 이 기준에 맞춰 코드를 작성하고 있습니다.
#define
#define 선행처리 지시자는 함수나 상수를 단순화해주는 매크로를 정의할 때 사용합니다.
매크로는 함수나 상수에 이름을 붙임으로써, 해당 매크로가 무엇을 가리키고 있는지를 명확하게 나타내 줍니다.
따라서 코드의 가독성을 증가시키고, 코드를 훨씬 더 읽기 편하게 해줍니다.
#define 지시자의 문법은 다음과 같습니다.
문법
#define 식별자 대체리스트
선행처리기는 #define 선행처리 지시문의 식별자(identifier)를 단순히 대체 리스트(replacement-list)로 치환해 주기만 합니다.
이러한 과정을 매크로 확장(macro expansion)이라고 합니다.
#define 선행처리 지시문에서 식별자는 매크로(macro)라고 부르는 사용자가 미리 정의한 약어입니다.
이때 매크로 이름인 식별자는 C언어의 변수 이름 생성 규칙과 똑같은 생성 규칙을 따라서 작성해야 합니다.
따라서 매크로 이름 중간에는 공백을 넣을 수 없지만, 실제값은 공백을 가질 수 있습니다.
또한, 매크로끼리 중첩하여 사용할 수도 있습니다.
C언어의 변수 이름 생성 규칙에 대한 더 자세한 사항은 C언어 변수 수업에서 확인할 수 있습니다.
예제
#include <stdio.h>
#define PI 3.14
int main(void)
{
double radius = 12;
printf("원주율을 나타내는 PI의 값은 %.2f입니다.\n", PI);
printf("원의 면적은 %.2f * %.2f * %.2f = %.2f입니다.\n", PI, radius, radius, PI * radius * radius);
return 0;
}
실행 결과
원주율을 나타내는 PI의 값은 3.14입니다.
원의 면적은 3.14 * 12.00 * 12.00 = 452.16입니다.
위의 예제에서 PI라는 매크로는 3.14로 전부 단순 치환됩니다.
하지만 문자열에 포함된 PI라는 문자는 이러한 치환 작업에서 제외됩니다.
위처럼 값을 나타내는 매크로를 객체 같은 매크로(object-like macro)라고도 합니다.