8 장전처리기 박종혁교수 UCS Lab Tel: 970-6702 Email: jhpark1@seoultech.ac.kr SeoulTech 2017-1 st 프로그래밍입문 (1)
2 전처리기 C 언어는전처리기를사용하여그능력과표기법을확장함 # 으로시작하는행을전처리지시자라고함 #include #define #if #error ANSI C 에서 # 은여백문자다음에올수있지만, 전통적인 C 에서는첫번째열에 # 이와야만함 전처리지시자가영향을미치는범위 그파일에서전처리지시자가있는위치에서시작여그파일의끝까지 다른지시자에의해서그지시자의효력이없어질때까지
전처리기 (preprocessor) 는컴파일하기에앞서서소스파일을처리하는컴파일러의한부분 #include, #define 만처리합니다. 수고했어, 나머지는나한테맡겨! 소스파일 임시파일 오브젝트파일 전처리기 컴파일러
4 전처리기 - 컴파일러는목적파일을만들기전에전처리라고하는특별한작업을 먼저수행한다. - 전처리과정에서소스파일에다른파일의텍스트를포함시키거나일부문장을다른문장으로바꾸는작업등을수행한다. - 전처리명령어는 # 기호로시작한다. - 전처리가끝난파일역시소스파일과마찬가지로텍스트파일이다.
전처리기의요약 지시어 #define #include #undef #if #else #endif #ifdef #ifndef #line #pragma 의미매크로정의파일포함매크로정의해제조건이참일경우조건이거짓일경우조건처리문장종료매크로가정의되어있는경우매크로가정의되어있지않은경우행번호출력시스템에따라의미가다름
6 #include 지정된파일을읽어드림 #include filename #include <filename> #include 에명시되는파일의내용은제한이없음 또한그파일은전처리기에의해다시확장되어야하는또 다른전처리지시자를포함할수도있음
7 #include #include filename 이행은 filename 파일의사본으로대치됨 filename 파일은먼저현재디렉토리에서검색하고, 거기에없다면시스템이정의한디렉토리에서검색함 #include <filename> 이것은시스템이정의한디렉토리에서만검색함 UNIX 시스템에서 stdio.h 와 stdlib.h 같은표준헤더파일은 /usr/include 에있음 일반적으로, 표준헤더파일이저장된장소는시스템에따라다름
8 #include 헤더파일을사용하면프로그램을깔끔하고편리하게작성할수있다. - 여러프로그램에서공통적으로사용하는구조체나함수또는외부변의선언을헤더파일로만들어놓고각프로그램에서간단히포함하여사용한다. myheader.h 로작성하여현재의작업디렉토리에저장해둔다.
9 전처리기에의한매크로처리 #define 으로시작하는전처리기지시자 컴파일러에의해처리되는것이아니다. 전처리기에게단순치환작업을요청할때사용되는 지시자
10 #define 두가지형식 #define identifier token_string opt // 단순매크로 #define identifier(id_1,..., id_n) token_string opt // 함수매크로 정의가길어질경우에현재행의끝에역슬래시 \ 를삽입하면, 다음행에연결해서계속쓸수있 음
단순매크로 단순매크로 (macro): 숫자상수를기호상수로만든것 ( 예 ) #define MAX_SIZE 100 #define PI 3.141592 #define EPS 1.0e-9 100 보다는 MAX_SIZE 가이해하기쉽지..
12 #define define 명령어는매크로명을정의하여복잡한상수나문장을의미있 는단어로사용할수있도록한다. - 컴파일러는전처리과정에서프로그램에서사용된매크로명을확장문자열로치환한다. #include <stdio.h> #define PI 3.14159 int main() { double radius; } 전처리후 printf(" 원의반지름을입력하세요 : "); scanf("%lf", &radius); // 이곳에는 stdio.h 헤더파일의내용이포함된다. int main() { double radius; printf(" 원의반지름을입력하세요 : "); scanf("%lf", &radius); printf(" 원의둘레 : %lf\n", 2*3.14159*radius); printf(" 원의둘레 : %lf\n", 2*PI*radius); printf(" 원의면적 : %lf\n", 3.14159*radius*radius); printf(" 원의면적 : %lf\n", PI*radius*radius); return 0; // 매크로명이상수값으로치환 return 0; }
단순매크로의장점 프로그램의가독성을높인다. 상수의변경이용이하다. 기호상수를사용하는경우 숫자를사용하는경우
14 예제 #include <stdio.h> #define PI 3.14159 // 매크로상수정의 #define LIMIT 100.0 // 매크로상수정의 #define MSG "passed!" // 문자열을매크로명으로정의 #define ERR_PRN printf(" 범위를벗어났습니다!\n") int main() { double radius, area; } // 출력문을매크로명으로정의 printf(" 반지름을입력하세요 : "); scanf("%lf", &radius); // 반지름입력 area=pi*radius*radius; // 면적계산 if(area>limit){ // 면적이한계값을넘으면 ERR_PRN; // 에러메시지를출력한다. } else{ printf(" 원의면적 : %.2lf (%s)\n", area, MSG); } // 그렇지않으면면적과문자열출력 return 0;
15 #define identifier t_string opt 전처리기는문자열을제외한파일의모든 identifier를 token_string으로대치 예제 #define SECONDS_PER_DAY (60 * 60 * 24) 이예제에서 token_string은 (60 * 60 * 24) 이고, 전처리기는그파일의이다음부터발견되는기호상수 SECONDS_PER_DAY 모두를 (60 * 60 * 24) 로대치함
16 #define identifier t_string opt #define을사용하면프로그램의명확성과이식성을높일수있음 #define PI 3.14159 #define C 299792.458 /*speed of light*/
17 #define identifier t_string opt 특수한상수들도기호상수로코딩될수있음 #define EOF (-1) /* typical end-of-file value */ #define MAXINT 2147483647 /* largest 4-byte integer */
18 #define identifier t_string opt 기호상수는불명확한상수를연상기호식별자로 바꿈으로써프로그램의문서화에도움을줌 시스템에따라달라지는상수를한번에변경할수 있으므로이식성을높여줌 상수의실제값을검사하는데한곳만검사하면 되므로신뢰성도높여줌
19 구문변경 C 의구문을사용자의취향에맞게변경하는것이가능 예제 논리수식에서 == 대신 EQ 사용 #define EQ == while 문을 ALGOL 형식의 while do 로변환 #define do /* blank */
20 인자를갖는매크로 일반적인형태 #define identifier(id_1,..., id_n) token_string opt 첫번째 identifier 와왼쪽괄호사이에는공백이없어야함 매개변수목록에는식별자가없거나또는여러개가올수있음
함수매크로 ( 인자를갖는매크로 ) 함수매크로 (function-like macro) 란매크로가함수처럼매개변수를가지는것 ( 예 ) #define SQUARE(x) ((x) * (x)) #define SQUARE(x) ((x) * (x)) 전처리기 v = SQUARE(3); v = ((3)*(3));
22 인자를갖는매크로 동일한수식이나문장에대하여경우에따라각각다른값으로확장될 수있도록만든것이매크로함수이다. - 전처리과정에서전달인자에따라서로다른문자열로확장된다. #define SUM(x, y) x+y int x=10, y=20; int a=100, b=200; printf( x+y = %d\n, SUM(x, y)); printf( a+b = %d\n, SUM(a, b));
23 함수매크로의장단점 함수매크로의장단점 함수호출단계가필요없어실행속도가빠르다. 소스코드의길이가길어진다. 간단한기능은매크로를사용 #define MIN(x, y) #define ABS(x) ((x) < (y)? (x) : (y)) ((x) > 0? (x) : -(x)) 매크로를한줄이상연장하는방법 #define PRINT(x) if( debug==1 && \ mode==1 ) \ printf( %d, x);
24 인자를갖는매크로 예제 #define SQ(x) ((x) * (x)) SQ(7 + w) 는 ((7 + w) * (7 + w)) 로확장 SQ(SQ(*p)) 는 ((((*p) * (*p))) * (((*p) * (*p)))) 로확장
25 인자를갖는매크로 유의사항 매크로를정의할때올바른평가순서를유지하기위해괄호를적절히사용해야함 부적절한괄호를사용한예 #define SQ(x) x * x : SQ(a + b) ==> a + b * a + b ((a + b) * (a + b)) #define SQ(x) (x) * (x) : 4 / SQ(2) ==> 4 / (2) * (2) ( 4 / ((2) * (2))
26 인자를갖는매크로 유의사항 실수할수있는또다른예제 #define SQ (x) ((x) * (x)) : SQ(7) ==> (x) ((x) * (x)) (7) #define SQ(x) ((x) * (x)); /* error */ : if (x == 2) x = SQ(y); else ++x; ==> if (x == 2) x = ((y) * (y));; else ++x;
27 인자를갖는매크로 유의사항 매크로함수는확장후에다른연산자와의추가적인연산에의해서부 작용이생길수있다. #define MUL(x, y) x*y printf( %d\n, 30/MUL(2, 5)); // 전처리되기전의코드 printf( %d\n, 30/2*5); // 전처리된이후의코드 나눗셈이먼저연산된다! 확장후의부작용을막기위해서확장문자열에괄호를사용한다. #define MUL(x, y) ((x)*(y)) // 확장문자열의각문자를모두괄호로감싼다. printf( %d\n, 30/MUL(2, 2+3)); printf( %d\n, 30/((2)*(2+3)));
28 예제 #include <stdio.h> #define PI 3.14159 #define SQUARE(x) ((x)*(x)) // 매크로상수정의 // 제곱을구하는매크로함수정의 int main() { double radius; } printf(" 반지름을입력하세요 : "); scanf("%lf", &radius); printf(" 원의면적은 : %.2lf", PI*SQUARE(radius)); return 0; // 매크로상수와매크로함수를사용하여원의면적을구한다.
29 인자를갖는매크로 매크로를정의할때, 매크로몸체에매크로나함수를사용할수있음 #define min(x, y) #define min4(a, b, c, d) #define SQ(x) #define CUBE(x) (((x) < (y))? (x) : (y)) ((x) * (x)) (SQ(x) * (x)) #define F_POW(x) sqrt(sqrt((cube(x))) min(min(a, b), min(c, d))
30 인자를갖는매크로 #undef 는매크로정의를무효화함 #undef identifier 전처리기결과보기 cc -E file.c 이명령을사용하면, 전처리기가작업을수행한다음에더이상의컴파일이일어나지않음
31 stddef.h 이헤더파일은다른곳에서공통적으로사용되는몇가지형정의과매크로를포함하고있음 예 typedef int ptrdiff_t; /* pointer difference type */ typedef short wchar_t; /* wide character type */ typedef unsigned size_t; /* the sizeof type */ #define NULL ((void *) 0)
32 stdio.h 의매크로 getc() 와 putc() 함수 getc() : 파일로부터한문자를읽음 putc() : 파일에한문자를출력 getchar() 와 putchar() 매크로 #define getchar() getc(stdin) #define putchar(c) putc((c), stdout)
33 ctype.h 의매크로 매크로 isalpha(c) isupper(c) islower(c) isdigit(c) isalnum(c) isxdigit(c) isspace(c) ispunct(c) isprint(c) isgraph(c) iscntrl(c) isascii(c) 참값 (0이아닌값 ) 이리턴되는경우 c가문자일때 c가대문자일때 c가소문자일때 c가숫자일때 c가영문자나숫자일때 c가 16진숫자일때 c가공백문자일때 c가구두문자일때 c가인쇄가능한문자일때 c가공백이아닌인쇄가능한문자일때 c가제어문자일때 c가 ASCII 코드일때
34 ctype.h 의매크로 함수또는매크로리턴되는값 toupper(c) tolower(c) toascii(c) c 가소문자이면대응되는대문자, 아니면 c c 가대문자이면대응되는소문자, 아니면 c c 와대응되는 ASCII 코드값
35 조건부컴파일 조건부컴파일을위한지시자 #if... #endif constant_integral_expression #ifdef identifier // or #if defined(identifier)... #endif #ifndef identifier... #endif
36 조건부컴파일 코드가컴파일되기위해서는 #if 다음의상수수식이영이아닌값 ( 참 ) 을가져야함 #ifdef나 #if defined와 #endif 사이의코드가컴파일되기위해서는 identifier가이미정의되어있어야하고, 그 identifier가취소 (#undef identifier) 되지않았어야함 #ifndef와 #endif 사이의코드가컴파일되기위해서는 #ifndef 다음의 identifier가현재정의되어있지않아야함
37 조건부컴파일예제 이식성이높은코드를만들기위해사용할수있음 #if defined(hp9000) defined(sun4) &&!defined(vax)... /* machine-dependent code */ #endif
38 조건부컴파일예제 디버깅을위해사용할수있음 #define DEBUG 1.... #if DEBUG debugging code #endif #define DEBUG.... #ifdef DEBUG debugging code #endif
39 조건부컴파일예제 디버깅을위해사용할수있음 statements /* more statements */ and still more statements statements #if 0 more statements #endif and still more statements
40 조건부컴파일예제 매크로이름이중복지정되는것을피하기위해사용할수있음 #include "everyting.h" #undef PIE #define PIE... "I like apple."
41 조건부컴파일 if-else 형의구조 #if #elif constant_integral_expression #else #endif
42 미리정의된매크로 ANSI C 에는미리정의된다섯개의매크로가있고, 이매크로는항상사용할수있으며, 프로그래머가정의를해제할수없음 각매크로이름은두개의밑줄문자로시작해서두개의밑줄문자로끝남 미리정의된매크로 DATE FILE LINE STDC TIME 값현재날짜를포함하는문자열파일이름을포함하는문자열현재라인번호를나타내는정수 ANSI C 표준을따르는경우 0이아닌값을가짐현재시간을포함하는문자열
43 # 연산자 " 문자열화 " 연산자 #define message_for(a, b) \ printf(#a " and " #b ": We love you!\n") int main(void) { message_for(carole, Debra); return 0; }
44 ## 연산자 토큰결합연산자 #define X(i) x ## i X(1) = X(2) = X(3); 전처리기수행후의결과 : x1 = x2 = x3;
45 assert() 매크로 수식의값이기대하고있는값인가를확인할때사용 #include <assert.h> void f(char *p, int n){... assert(p!= NULL); assert(n > 0 && n < 7):... 만일단정이실패하면, 시스템은메시지를출력 하고프로그램을중단함 NDEBUG 가정의되어있으면, 모든단정은무시됨
46 #error 조건들을강요하기위해사용 전처리기가 #error 를만나면, 컴파일오류가발 생하고, 이지시자다음에쓰인문자열이화면에 출력됨 사용예 #if A_SIZE < B_SIZE #endif #error "Incompatible sizes"
47 행번호 사용형태 #line integral_constant "filename" 이것은컴파일러로하여금원시프로그램의행번호를다시매기게하여그다음행의번호가 integral_constant 가되게함 또한컴파일러에게현재원시파일의이름이 filename 이라고믿게함 파일이름이명시되지않으면, 행번호만을다시매김 보통행번호는프로그래머에게감추어지고, 경고나구문오류가발생될때에만나타남
48 대응함수 표준헤더파일에있는매개변수를갖는대부분의매크로는이와대응되는함수를표준라이브러리에가지고있음 매크로대신함수를사용하기위해서는다음과같이함 방법 1 - 함수사용전에다음과같은행을삽입함 #undef isalpha 방법 2 - 다음과같이호출함 (isalpha)(c)
49