일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- OS 커널
- UNIX
- kernel
- 포인터
- Windows via c/c++
- TiDB
- Pointer
- Golang
- getopts
- newSQL
- 커널
- FreeBSD
- SQLite
- 전처리기
- 구조와 원리
- DBMS
- Preprocessor
- 포인터변수
- bash
- UNIX Internals
- 컴퓨터 강좌
- TiKV
- 긴옵션
- Symbol
- Programming
- DBMS 개발
- go
- 약어
- 한빛미디어
- 함수포인터
- Today
- Total
sonumb
C Preprocessor 종합 선물세트 본문
목차.
1.File Inclusion
2.Macro Substitution
3.Contitional Inclusion
4.기타
Preprocessor의 핵심:
`Preprocessing`은 링크전도 아닌 컴파일타임도 아닌 컴파일타임 전에 일어난다는 것이다.
1.File Inclusion
제목을 영어로 거창하게 적어놨지만 역시 핵심은 파일을 갖다 붙이겠다는 말이다.
#include <filename> or #include "filename"
<,>로 묶여진 파일은 implementation-defined rule에 의해 해당파일을 찾는다.
","로 묶여진 파일은 보통 해당파일(즉 #include "filename"이 쓰여진 파일)이 있는 폴더내에서 찾는다.
2.Macro Substitution
#define
#define 은 이래 저래 쓸데가 많다.
#define SQUARE(x) ((x)*(x)) // Macro Function
#define _OUR_HEADER // 보통 Condition 매크로랑 같이 쓰인다.
#define PI 3.14159 // Macro Constant
#undef
아래의 예문만 봐도 이해가 간다.
#define PI 3.14159
#undef PI
..
printf("%lf",PI); // error!!
#, ##
위의 두개는 문자열과 관계있고, #define 다음에 와야 한다. 소스내에 있으면 syntax 에러발생.
아래의 예제를 보면 이해 될 것이다.
#include <stdio.h>
/*
* ##전후에 tab이나 공백있을시 무시하며 앞뒤 문자열을 붙여준다.
* 문자열을 붙여주긴 하되, 문자열상수로 만들어주진 않는다.
* 즉 아래의 예제는 lstr[i]로 된다. "lstr[i]" 가 아닌거임. -_-;
*/
#define GLUE(lstr, i) lstr ##[## i ##]
//var 자체가 string 즉 문자열 상수로 치환된다.
#define MakeStringr(var) #var
int main( void )
{
int arr[3] ={ 0,1,2};
printf(
MakeString( arr[1] )
" :%d\n", GLUE( arr, 1 )
);
return 0;
}
위의 메인 소스는 컴파일러( 정확하게 말하자면 전처리 해석기)에 의해 소스가 다음과 같이 치환(!)된다.
int main( void )
{
int arr[3] ={ 0,1,2};
printf(
"arr[1]"
" :%d\n", arr[1]
);
return 0;
}
그러고는 컴파일을 하겠지?
이제는 짐작이 올것이다.
결과는
arr[1] :1
3.Conditional Inclusion
조건처리에 관한것이다. if( condition ) ... else if( condition ) ... 와 비슷하다.
#define _ABC
#define _BBC
// 참고: #ifndef _ABC -->#if !defined(_ABC)
#ifdef _ABC // #if defined(_ABC)
// 위 조건에 따라 이것이 선택된다.
#define GLUE(lstr, i) lstr[##i]
#else if (!defined( _ABC) && defined( _BBC ))
#define GLUE(lstr, i) *(lstr+##i) // 선택이 되지 않는다.
#endif#define MakeString( var ) #var
#ifndef ,#ifdef 과 #if 와의 차이는 여러 조건을 넣을 수 있느냐 없느냐다.
#else if (!defined( _ABC) && defined( _BBC )) 이것 처럼
이런 것도 된다.
#define TRUE 1
#define FALSE 0
#define _ABC TRUE
#if _ABC == TRUE
#define GLUE(lstr, i) lstr[##i]
#elif _ABC == FALSE
#define GLUE(lstr, i) *(lstr+##i)
#endif
아래예제는 안된다. 전처리기는 문자열 비교 자체가 안되기에 컴파일시 Error가 발생한다.
//#define _ABC
#define _BBC "true"#ifdef _ABC // #if defined(_ABC)
#define GLUE(lstr, i) lstr[##i]
// 당연히 안된다.. 문자열 비교와 어떻게 같은가?
#elif _BBC == "true"
#define GLUE(lstr, i) *(lstr+##i)
#endif
결론은 전처리프로세서는 멋지다는 것이다. 쿨럭.;;
농담이고..
조건 비교와 조건 비교시 상수 비교, 논리산술(and, or , not) 정도는 된다것을 기억해두면된다.
4.기타
한꺼번에 때려 넣어 봤다.
몇가지 directive 랑 매크로 상수(Macro Constant)에 관한 내용이다.
매크로 상수는 __name__ 와 같은 형태이다.
그 이외에도 #using 이나 #pragma 등이 있다. using은 `c++ 전처리기`라서, pragma는 다음과 같은 이유로 설명을 하지 않겠다. 대체적으로 표준이 아니라 운영체제 종속적이거나, 컴파일러 제조사의 취향(?)에 맞게 directive가 설계되어지는 것이기에(implementation-defined) 차라리 제조사 Manual을 보는게 맞다.#include <stdio.h>
// `다음 라인`은 43번이다! 그리고 소스파일의 이름은 `new.c`이다
// 라고 '컴파일러'에게 알려준다.
#line 43 "new.c"
// 해당 컴파일러가 C 컴파일러인지에 대한 매크로 상수
// 컴파일러가 C++컴파일러면 __STDC__ 안의 매크로가 실행될것이다.
#ifndef __STDC__
/*
* 에러 난경우 IDE나 콘솔에
* #error 다음 개행 전까지의 문자열을 출력
* 사실 C Compiler가 아닌 거임. <- 이것이 출력된다.
*/
#error 사실 C Compiler가 아닌 거임.
#endifint main( void )
{
printf(
"Time: %s\n"
"Timestamp: %s\n"
"Date: %s\n"
"Line: %d\n"
"File: %s\n",
__TIME__, //현재 소스파일이 가장 최근 컴파일 된 시간.
__TIMESTAMP__ , //현재 소스파일이 마지막으로 수정된 시간.
__DATE__, // 현재 소스 컴파일 한 날짜.
__LINE__, // 현재 __LINE__ 매크로 상수가 위치한 라인번호
__FILE__ // 현재 소스파일의 이름.설정에 따라 path를 포함하기도 한다.
);return 0;
}
결론을 내려보자
매크로는 편리하면서도 화려하게 만들수 있으나, 나중에 되면 오히려 혼란만 가중시키게 되는 복잡성도 가지고 있다.( Condition Inclusion 같은...)매크로 펑션처럼 간단하면서도 편리하지만 그 이면에는 디버깅이 안된다는 해악도 같이 존재한다. 또한 말그대로 Preprocessor이기에 프로젝트 build시 컴파일 부담을 가중시킬수도 있다.
아무쪼록 평소에 간단한 퀴즈 같은 프로그램들을 많이 짜보면서 손익을 스스로 체득하는 것이 바람직하겠다.