본문 바로가기
컴퓨터

[C언어] 전처리기

by Luyin 2012. 11. 9.

선행처리 또는 전처리, 영어로  PreCompiler  또는 PreProcess 라고 한다...


이하 전(前)처리기로 하겠다.... 전처리기의 개념을 잡기 위해서 먼저 컴파일 되는 과정을 살펴보자..




C언어의 컴파일은 위와 같은 과정으로 수행이 된다


C언어로 소스코드를 작성하면 전처리 코드를 처리하여 최종적인 코드로 재조합 한 다음...


컴파일을 통해서 목적파일(object file)을 만들고... Object들을 한데 모아서.. 서로 결합(Link)을 하면


실행파일이 생성되는 것이다....


전처리기는 매크로, 코드포함관계, 코드 내의 컴파일 옵션 등을 재조합 하는 역할을 한다..


맨 앞에 #(샵) 으로 시작하는 코드들은 모두 전처리기 이다...


전처리기 명령들은 여러가지가 있는데...


대표적으로 가장 많이 사용하는 전처리기는 아래와 같다..


#include : 특정파일의 코드 포함

#define : 매크로 정의

#undef : 정의된 매크로를 해제

#if, #ifdef, #ifndef, #else, #endif : 전처리 조건문


그 밖에

#pragma : 컴파일 옵션

#import : include와 같은 포함이지만 중복 포함을 자동으로 걸러줌.(아주 예전 컴파일러는 안먹음)


이 외에도 필자 머리속에 없는 전처리기가 있을법 한데 기억이 잘 안난다...


나중에 인터넷 웹서핑이나 책을 보고 찾아보기 바란다.... ㅋㅋㅋㅋ 난 모른다...


암튼... 위와 같은 종류의 전처리기 들이 있다....


자!! 그럼 include 부터 설명을 해볼까나??


include는 사전적인 의미로 "포함하다" 라고 해석이 되어지며...


C언어를 조금이라도 맛을 본 사람이라면.. "아!! 저거 헤더파일 선언할때!!" 라고 할것이다...


하지만 include는 헤어파일 선언 뿐만 아니라... "소스 자체를 포함시킬때" 라는 것이 더 정확한 표현일 것이다....


예를 들어 설명하는 것이 빠르겠다...


preprocess.c


 printf ( "이것은 전처리기 테스트입니다.\n" )



위에 처럼 preprocess.c 라는 파일에 "이것은 전처리기 테스트입니다" 라는 문구를 넣었다...



example.c


... (각종 해더파일 알아서 작성하시라)

main()

{

#include "preprocess.c"

}



위와 같이 두개의 파일을 하나의 폴더(디렉토리) 내에 작성을 하고 컴파일을 해 보라...


분명 컴파일이 정상적으로 된다...


보이는가?? include라는 것은 컴파일 전에 다른 파일의 코드를 포함시키는 것이다....


전처리 후에는 아래와 같이 변경이 되어서 컴파일 된다..


그렇다고 소스 파일 자체가 변경되는것은 아니고 컴파일러에게 


소스코드를 전달할때 아래와 같이 재조합하여 컴파일을 한다는 이야기다...


example.c  -- 전처리 후


... (각종 해더파일)

main()

{

 printf ( "이것은 전처리기 테스트입니다.\n" )

}





자!! 이제 이해가 가는가 include 라는 것을...


C언어를 쬐금 맛본 사람들은 include가 헤더파일 선언하는 것인줄 알겠지만...


실제로 헤더파일 선언이 아니라 외부 파일을 포함시키는 것이다...


include가 가장 활용도가 높은 곳이 헤더파일이기 때문에 보통은 헤더파일을 선언할때 많이 쓴다...


그러니까 헤더파일의 내용들을 컴파일 하기전에 해당 소스코드에 쫘악~~~ 깔아준다는 말이다...


include로 파일을 지정하는 방법은 아래와 같이 두가지가 있는데..  


#include <파일> 

#include "파일"


첫번째 <파일> 요놈을 사용하면 컴파일 옵션을 지정할때 정의해 놓은 위치에서 파일을 찾게 된다..


두번째 "파일" 요놈은 절대적 위치를 주거나... 혹은 같은 폴더(디렉토리)안에 파일을 찾게 된다...


헌데 같은 폴더안에 파일이 없다면... <파일> 요놈이랑 같은 위치를 찾게 되지..


그러니까 같은 폴더안에 파일이 없으면 컴파일 옵션에서 지정한 위치에서 파일을 찾게 된다는 말이다...


#include <stdio.h> 


위와 같이 저런거 많이 봤을거다... 앞서 C언어 기본함수에 관해서 배웠는데....


아무짓 안하고 컴파일을 하게 되면 기본적으로 C언어가 설치된 곳의 헤더와 라이브러리를 참조하게 된다...


stdio.h 를 기본적으로 지정된 헤더파일 폴더에서 찾으라는 뜻이 되겠다....


파일이 존재한다면... stdio.h 라는 파일 안에 내용을 컴파일전 쫘악~~ 깔아 줄테고...


stdio.h라는 파일이 없다면.. 컴파일 오류를 내게 될것이다....




자 이제 define를 해보자..


전처리기 define은 매크로 라고도 표현한다...


매크로가 무엇인고하니?


네이버에 검색해보니 아래와 같은 답변을 주더군...


자주 사용하는 여러 개의 명령어를 묶어서 하나의 키 입력 동작으로 만든 것을 매크로라고 한다. 여러번 해야 하는 일을 간단하게 수행하기 위하여 사용하기도 하지만, 문서 안의 같은 문자열을 한꺼번에 변경 할 때도 사용된다.


자 그럼 C언어에서의 매크로란 무엇인가??


간단히 이야기 해서 프로그래밍을 위한 프로그래밍이라 하겠다..


프로그램 코드 내에서 자주 사용하는 문맥들을 간결하게 정의 함으로서 프로그램할때 덜 헛갈리게 되고..


또 프로그래밍 하다보면 상수등을 한꺼번에 바꿔야 할때.. 매크로를 이용하면 졸라게 쉽게 된다...


define의 사용방법은


#define [매크로 이름] [매크로내용]


위와 같이 하면 되겠다...


예를 들어보자... 



example.c

#define TEST_MACRO   1


main()

{

 printf ( "%d\n", TEST_MACRO );

}




위와 같이 TEST_MACRO라는 이름의 매크로는 1 을 지정하도록 하였다..


전처리기를 통해 컴파일 하기 전에 아래와 같이 변형이 된다...




example.c

//#define TEST_MACRO   1


main()

{

 printf ( "%d\n", 1 );

}



프로그래밍 하는 사람의 눈에는 변형된 과정이 보이지 않으나.. 컴파일러가 컴파일 하기전에..


매크로의 이름을 매크로 내용으로 자동으로 바꿔서 컴파일 해주는것이다..


위에서도 설명했지만.. 전처리기란 컴파일하기전에 include, define ... 등등을 프로그래밍하는 사람의 눈에는 과정이 보이지 않지만..


쫘악~~ 풀어서 깔아준 후 컴파일을 하게끔 하는 것이다...


define은 숫자를 써도 되고.. 문자열을 써도 되고... 때로는 함수처럼 사용할 수도 있다....


매크로를 함수처럼 사용하면 컴파일 하기전에 함수를 풀어서 쫘악~~깔아줌으로서...


실제 함수를 사용하는것보다 약간은 속도면에서 이득을 얻을 수도 있다...


아래 예를 보자..

example.c

#include <stdio.h> 
#define square(x) x*x 

int main(int argc, char **argv) 

    printf("square(3) : %d \n", square(3)); 

    return 0; 
}



전처리를 통해서 아래와 같이 변경되어 컴파일 된다.



example.c

#include <stdio.h> 
//#define square(x) x*x 

int main(int argc, char **argv) 

    printf("square(3) : %d \n", 3*3 ); 

    return 0; 
}


위에 3*3을 주시 하기 바란다... 코딩할때는 square(x) x*x라고 했지만..


실제 컴파일 할때는 매크로를 풀어서 깔아주게 된다... 물론 개발하는 사람 눈에는 과정이 보이진 않지만..


암튼... define는 코딩할때 아주 편리하게 해주는 도구이다...



example.c

#include <stdio.h> 
#define PRN printf("TEST\n")

int main(int argc, char **argv) 

    PRN;

    return 0; 
}


위와 같이 해도 된다.. PRN 자체를 printf("TEST\n") 로 정의를 해 놓았으므로...


실제 사용할때 PRN; 이렇게 사용하면 정의해 놓은 문장이 컴파일 전에 쫘악!! 깔린다...


define는 쉽게 생각하면 단순히 정의된 문장을 코드에 쫘악~~ 깔아 버리는 역할을 한다는 것이다..


define는 대충 정리가 되었고... 전처리 조건문을 보자..




전처리 조건문은


#ifdef

#ifndef

#if

#else

#endif


위와 같이 사용할 수 있다..


앞서도 계속 언급했지만 전처리는 컴파일이 되기전에 수행이 되므로.. 실행파일이 이미 


만들어졌다면 영향력이 없다...


전처리 조건문을 사용하는 용도는 대량으로 코멘트를 처리하기 위해서 사용하기도 하고..


플렛폼이 다른 컴퓨터 간에 크로스 컴파일이 되도록 하기 위해서도 사용하고..


기타 조건이 달라질때 컴파일만 해주면 동적으로 코드 내용을 주석처리 하는 효과를 내거나...


주석처리효과를 풀어주는 용도로 사용한다..


다소 말이 좀 복잡하지만.. #ifdef 부터 한번 보자..


기본적으로 #ifdef는 #endif 와 짝을 이룬다..


앞에 #if 로 시작하는 놈들은 모두 #endif와 짝을 이룬다는 뜻이기도 하다..


#if... 로 시작해서 #endif 까지를 하나의 블럭으로 본다....


아래 예를 보자..


example.c

#include <stdio.h> 

#define TEST_1

int main(int argc, char **argv) 
 

#ifdef TEST_1

    printf( "TEST_1"\n");

#endif

    printf( "ETC"\n") ;

    return 0; 
}


위의 코드를 설명하자면 define 매크로를 사용하여 TEST_1 이라는 것을 정의 했다.


물론 TEST_1이라는 것은 정의만 되어 있을 뿐... 매크로 값이나 기타 내용은 없다..


define은 저렇게 사용해도 된다...


#ifdef 는 매크로가 정의 되어 있으면 참. 정의 되어 있지 않으면 거짓이다..


define로 TEST_1이라는 것을 정의 했으므로.. 화면에 "TEST_1" 이라는 문자열을 찍어줄 것이다...



example.c

#include <stdio.h> 

//#define TEST_1

int main(int argc, char **argv) 
 

#ifdef TEST_1

    printf( "TEST_1"\n");

#endif

    printf( "ETC"\n") ;

    return 0; 
}


위와 같이 define를 코멘트 처리 해버리면... "TEST_1" 이라는 문자열은 화면에 나타나지 않는다...


#else 를 활용하는 방법도 있다..


아래와 같이 #else를 활용하여 어떤때는 "TEST_1"을 출력하고..


어떤때는 "ETC"를 출력하도록 할 수도 있다..


example.c

#include <stdio.h> 

#define TEST_1

int main(int argc, char **argv) 
 

#ifdef TEST_1

    printf( "TEST_1"\n");

#else

    printf( "ETC"\n") ;

#endif

    return 0; 
}




앞서 설명했지만 전처리는 컴파일 전에 일어나므로 실행파일이 만들어진 후 에는 영향력이 없다는것


항상 염두해 두길 바란다... 실행파일이 만들어지는 과정에서 간섭을 하는 것이 전처리다...


#ifndef 는 #ifdef와 반대의 개념을 가진다...


#ifndef는 매크로가 정의 되어 있지 않을때 참이 된다.... 위의 예제코드의 #ifdef를 #ifndef로 고치면...


반대로 작동할 것이다...


#ifdef 또는 #ifndef가 가장 활용도가 높을때는 헤더파일 선언할때이다...


#include는 포함이라고 했는데.. 보통은 헤더파일을 포함할때 많이 사용되는데..


프로젝트가 커지만 헤더파일안에 있는 변수 선언이라든지 이런 내용들이 중복으로 선언될 가능성이 크다..


그럴때 #ifdef 또는 #ifndef 가 빛을 발한다. 



test.h

#ifndef _TEST_H

#define _TEST_H

int a;

#endif



example.c

#include <stdio.h> 

#include <test.h>
#include <test.h>

int main(int argc, char **argv) 
 

    a=10;

    printf ( "%d\n", a )

    return 0; 

}


위와 같이 실수로 #include를 두번 할 수도 있고.. 또는 #include 내에 포함관계가 또 종속될 수도 있는 


경우가 생긴다. 저렇게 막!! 남발해도 #ifndef로  중복 허용을 막아 놓았으므로... 오류가 나지 않게 된다...


근데.. 프로그램 하다 보면 사용자 정의로 좀 더 다이나믹하게 전처리 조건을 바꾸고 싶을때도 있다..


예를 들어 프로그램을 데모버젼, 베타버젼, 정식버젼 이렇게 나누고 싶을때도 생긴다..


그럴때 #ifdef 를 사용해도 되지만.. 더 좋은게 있다..


바로 #if 이다.. #if 역시 #endif와 짝을 이룬다...


#if <조건>

..

..

#endif


위와 같이 사용할 수 있으며... <조건> 안에 정의된 수가 0 이면 거짓, 1이면 참이 된다..


#define VERSION 1


#if VERSION==1

..

..

#endif


위와 같이 하면 VERSION 1일때 블럭안에 것을 컴파일 하게 되고..


#define VERSION 2


#if VERSION > 1

..

..

#endif


위와 같이 하면 VERSION 이 1 보다 클때 블럭 안에 것을 컴파일 하게 된다...


이렇듯 전처리 조건문을 잘 사용하면 크로스 컴파일 까지 가능하도록 만들 수 있을 것이다...



pragma를 설명해 주고 싶어도... 워낙 내용이 방대 하므로... 걍 넘어가자...


나중에 인터넷 찾아보도록 해라..


출처 :http://blog.naver.com/bhcastle?Redirect=Log&logNo=80161010974