Preprocessors in C language
C 언어 전처리기 ( Preprocessors )
C 언어에서 전처리기는 #으로 시작되는 키워드를 말한다. 예를 들면, #include, #define 등과 같은 것인데 쉽게 설명하자면 코드를 컴파일하기 전에 코드 중에서 전처리 키워드가 사용된 부분을 미리 지정한 텍스트로 대치하는 기능을 가진다.
전처리 명령어는 함수 안이나 밖에서도 사용할 수 있다
주요 전처리 명령어
전처리 명령 (Directives) |
기능 |
#define |
전처리 매크로가 사용된 부분을 지정된 코드로 대치한다 |
#include |
다른 파일에 저장된 헤더파일을 현재 파일에 포함한다 |
#undef |
전처리 매크로를 해제한다 |
#ifdef |
전처리 매크로가 선언되어 있으면 true 를 리턴한다 |
#ifndef |
전처리 매크로가 선언되지 않았으면 true 를 리턴한다 |
#if |
조건이 true 인지 검사한다 |
#else |
#if 의 조건이 false 이면 실행된다 |
#elif |
#if, #elif, #else는 조건을 검사할 때 함께 사용된다 |
#endif |
#if 의 종료 |
#error |
stderr 를 이용하여 오류 메시지를 출력한다 |
#pragma |
컴파일러에게 특별 명령을 전달한다 |
전처리 명령의 예
#define PI 3.141592
#define 전처리 명령은 프로그램의 가독성( readability )을 위해 프로그램 코드의 특정 부분에 매크로를 사용하고 컴파일 전에 실제 코드로 대치할 때 사용된다
위의 경우는 프로그램 코드 중에 PI 라는 전처리 매크로를 사용한 곳이 있으면 3.121592로 대치한다
#include <stdio.h>
#include "myheader.h"
현재 코드에 지정된 헤더파일을 추가한다
라이브러리에 컴파일되어 있는 함수나 다른 개발자가 작성한 함수를 사용하기 위해서는 현재의 코드에 해당 함수를 선언할 필요가 있는데, 이 때 많은 함수가 사용된다면 헤더파일에 함수를 선언하고 그 헤더파일을 현재의 코드에 포함하면 간편하다
<stdio.h>는 표준 시스템 헤더파일을 포함하는 경우이고 "myheader.h" 는 현재의 코드와 같은 곳에 헤더파일이 있는 경우이다
#undef RATIO 1.2
#define RATIO 2.3
이미 선언된 전처리 매크로 RATIO를 해제하고 새로 선언할 때 매크로를 선언한다. 이로 인해서 RATIO 라는 전처리 매크로가 사용된 곳은 컴파일될 때 2.3으로 대치된다
#ifndef MAIN_HEADER
#define MAIN_HEADER
#include "mainheader.h"
#endif
MAIN_HEADER 라는 매크로가 이미 정의되어 있지 않으면 MAIN_HEADER 라는 매크로를 정의하고 "mainheader.h" 파일을 포함한다
다수개의 코드파일로 구성된 프로그램에서는 코드파일 마다 공통 헤더파일을 포함하는 경우가 있는데, 소스코드를 컴파일하여 링커에 의해서 컴파일된 코드가 합쳐질 때 동일한 헤더파일이 중복되어 포함되는 문제가 있으므로 헤더파일을 한번만 포함하도록 위와 같은 전처리 명령을 사용할 필요가 있다
#ifdef DEBUG
printf("%s \n", message);
#endif
DEBUG 매크로가 선언된 경우에만 화면에 특정 변수의 값을 출력하고자 할 때 사용한다
주로 개발자가 디버깅 목적으로 개발시에만 특정 코드 부분을 실행하기 위해서 사용되며 개발이 완료되면 DEBUG 라는 매크로를 제거하여 해당 부분이 실행되지 않도록 할 수 있다
미리 내장된 전처리 매크로 (Predefined Macros)
ANSI C 언어는 개발의 편의성을 제공하기 위해 몇개의 전처리 매크로를 내장하고 있다
__DATE__ : 현재 날짜
__TIME__ : 현재 시간
__FILE__ : 현재 소스파일의 파일명
__LINE__ : 현재 코드의 행번호를 10진수로 표현
__STDC__ : 현재의 컴파일러가 ANSI C 컴파일로 설정되어 있는 경우에 1 로 설정됨 ( VC 에는 없음 )
전처리 연산자, 매크로 연속 연산자( '\' ), 역슬래시
일반적으로 매크로는 실제 컴파일 시에 대치될 내용을 표현하며 한 행으로 나타낼 수 있다. 그러나 매크로가 한 행을 넘어서 정의되는 경우에는 특별한 연산자(매크로 연속 연산자, '\' )를 사용하여 매크로가 한 행을 넘어 그 다음 행도 매크로가 선언되고 있다는 것을 표시해야 한다
참고 : 아래의 코드를 보기 전에 printf("A" "B") 형태의 문장도 문법적으로 가능하다는 것을 이해하고 있어야 한다
전처리 연산자, 문자열화 연산자 ( Stringize Operator )
매크로 함수를 선언할 때 파라미터를 선언할 수 있으나 자료형은 사용할 수 없다. 매크로 파라미터를 사용할 때 파라미터로 전달한 값이 실제 코드에서 문자열 상수로 사용되기를 원한다면 전처리 문자열화 연산자( '#' )를 사용해야 한다. # 연산자는 단순하게 전달된 파라미터의 양쪽에 따옴표(" ")를 붙여서 문자열 상수로 변환한다. #연산자는 매크로 함수 안에서 파라미터를 문자열로 변환할 때만 사용된다
매크로 함수와 매크로 연속 연산자, 문자열화 연산자 사용 예
#define MSG(a, b) \
printf("param1=" #a", param2=" #b "\n");
혹은 아래처럼
#define MSG(a, b) \
printf("param1=%s, param2=%s \n", #a, #b);
토큰 결합 연산자 (##), Token Pasting Operator
매크로 연산자 '##' 은 좌우의 토큰을 단순히 결합하는 기능을 한다. 좌우의 토큰이 반드시 매크로의 파라미터일 필요는 없다
#include <stdio.h> #define showNum(n) printf("num" #n "=%d \n", num##n) int main() { int num1 = 100; showNum(1); return 0; }
위의 매크로 함수가 사용될 때 전처리기에 의해 변환되면 다음과 같은 형태가 될 것이다
printf("숫자=%d \n", num1);
그러므로 위의 문장이 실행되기 전에 num1 이라는 정수 변수가 존재해야 오류를 방지할 수 있다
전처리 defined() 연산자
전처리 조건( #if )을 구성할 때 사용되어 아규먼트로 전달된 매크로가 #define 에 의해 선언되어 있으면 true를 리턴한다
#if !defined( STDLIB )
#define STDLIB
#include <stdlib.h>
#endif
매크로 함수의 다른 예
#include <stdio.h> #define SWAP(a,b) \ {int c=a; a=b; b=c;} int main() { int x = 10; int y = 20; SWAP(x, y); printf("x=%d, y=%d \n", x, y); return 0; }
매크로 함수를 작성할 때 주의할 점
아래의 프로그램에 포함된 매크로 함수를 실행하면 원하는 결과가 출력되지 않는다
#include <stdio.h> #define SQUARE(n) n*n int main() { int res = SQUARE(3); printf("SQUARE(3)=%d \n", res); // 9 res = SQUARE(3+2); printf("SQUARE(3+2)=%d \n", res); //11 return 0; }
위의 매크로 함수는 다음과 같이 수정되어야 제대로 작동하게 된다
#define SQUARE(n) ((n)*(n))