쉽게풀어쓴 C 언어 Express 제 15 장전처리및비트연산
이번장에서학습할내용 전처리지시어 분할컴파일 명령어라인의매개변수 디버깅방법 전처리와기타중요한테마에대하여학습한다.
전처리기란? 전처리기 (preprocessor) 는컴파일하기에앞서서소스파일을처리하는컴파일러의한부분 #include, #define 만처리합니다. 수고했어, 나머지는나한테맡겨! 소스파일 임시파일 오브젝트파일 전처리기 컴파일러
전처리기의요약 지시어 #define #include #undef #if #else #endif #ifdef #ifndef #line #pragma 의미매크로정의파일포함매크로정의해제조건이참일경우조건이거짓일경우조건처리문장종료매크로가정의되어있는경우매크로가정의되어있지않은경우행번호출력시스템에따라의미가다름
단순매크로 단순매크로 (macro): 숫자상수를기호상수로만든것 ( 예 ) #define MAX_SIZE 100 #define PI 3.141592 #define EPS 1.0e-9 100 보다는 MAX_SIZE 가이해하기쉽지..
단순매크로 #define MAX_SIZE 100... while(i<max_size) { sum += i; i++; }... 전처리기... while(i<100) { sum += i; i++; }...
단순매크로의장점 프로그램의가독성을높인다. 상수의변경이용이하다. 기호상수를사용하는경우 숫자를사용하는경우
단순매크로의예 #define PI 3.141592 // 원주율 #define TWOPI (3.141592 * 2.0) // 원주율의 2배 #define MAX_INT 2147483647 // 최대정수 #define EOF (-1) // 파일의끝표시 #define MAX_STUDENTS 2000 // 최대학생수 #define EPS 1.0e-9 // 실수의계산한계 #define DIGITS "0123456789" // 문자상수정의 #define BRACKET "(){}[]" // 문자상수정의 #define getchar() getc(stdin) // stdio.h에정의 #define putchar() putc(stdout) // stdio.h에정의 214748364 7 보다는 MAX_INT 가낫죠 사람은숫자보다기호를잘기억합니다.
예제 #include <stdio.h> #define AND && #define OR #define NOT! #define IS == #define ISNOT!= int search(int list[], int n, int key) { int i= 0; && C 프로그램을다른언어처럼작성할수있습니다. while( i < n AND list[i]!= key ) i++; if( i IS n ) return -1; else return i; } int main(void) { int m[] = { 1, 2, 3, 4, 5, 6, 7 }; == } printf("%d\n", search(m, sizeof(m)/sizeof(m[0]), 5)); return 0;
중간점검 1. #define을이용하여서 1234를 KEY로정의하여보라. 2. #define을이용하여서 scanf를 INPUT으로정의하여보라.
함수매크로 함수매크로 (function-like macro) 란매크로가함수처럼매개변수를가지는것 ( 예 ) #define SQUARE(x) ((x) * (x)) #define SQUARE(x) ((x) * (x)) 전처리기 v = SQUARE(3); v = ((3)*(3));
함수매크로의예 #define SUM(x, y) ((x) + (y)) #define AVERAGE(x, y, z) (( (x) + (y) + (z) ) / 3 ) #define MAX(x,y) ( (x) > (y) )? (x) : (y) #define MIN(x,y) ( (x) < (y) )? (x) : (y)
주의할점 #define SQUARE(x) x*x // 위험!! v = SQUARE(a+b); v = a + b*a + b; 함수매크로에서는매개변수를괄호로둘러싸는것이좋습니다. #define SQUARE(x) (x)*(x) // 올바른형태
함수매크로의장단점 함수매크로의장단점 함수호출단계가필요없어실행속도가빠르다. 소스코드의길이가길어진다. 간단한기능은매크로를사용 #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);
예제 // 매크로예제 #include <stdio.h> #define SQUARE(x) ((x) * (x)) int main(void) { int x = 2; printf("%d\n", SQUARE(x)); printf("%d\n", SQUARE(3)); printf("%f\n", SQUARE(1.2)); printf("%d\n", SQUARE(x+3)); printf("%d\n", 100/SQUARE(x)); printf("%d\n", SQUARE(++x)); ((++x) * (++x)) // 실수에도적용가능 // 논리오류 x 23 4 } return 0; (4)*(4)=16 4 9 1.440000 25 25 16
# 연산자 PRINT(x) 와같이호출하면와같이출력하는매크로작성 x=5 다음과같이작성하면잘못된결과가나온다. #define PRINT(exp) printf("exp=%d\n", exp); exp=5
# 은문자열변환연산자 (Stringizing Operator) 라고불린다. 매크로정의에서매개변수앞에 # 가위치하면매크로호출에의하여전달되는실제인수는큰따옴표로감싸지고문자열로변환된다. #define PRINT(exp) printf(#exp" = %d\n",exp); PRINT(x); x=5
## 연산자 ## 연산자는토큰병합연산자 (token-pasting operator) #define MAKE_NAME(n) v ## n MAKE_NAME(1) 과같이호출된다고가정하자. 매개변수 n 은 1 로치환되고 ## 연산자에의하여 v 와 1 이합쳐져서하나의토큰 v1 이된다.
예제 #include <stdio.h> #define MAKE_NAME(n) v ## n #define PRINT(n) printf("v" #n " = %d\n", v ## n); int main(void) { int MAKE_NAME(1) = 10; // int MAKE_NAME(2) = 20; PRINT(1); // printf("v1 = %d\n", v1); 과같다. PRINT(2); // printf("v2 = %d\n", v2); 과같다. return 0; } v1 = 10 v2 = 20
내장매크로 내장매크로 : 미리정의된매크로 내장매크로 설명 DATE 이매크로를만나면현재의날짜 ( 월일년 ) 로치환된다. TIME 이매크로를만나면현재의시간 ( 시 : 분 : 초 ) 으로치환된다. LINE 이매크로를만나면소스파일에서의현재의라인번호로치환된다. FILE 이매크로를만나면소스파일이름으로치환된다. printf(" 컴파일날짜 =%s\n", DATE ); printf(" 치명적에러발생파일이름 =%s 라인번호 = %d\n", FILE, LINE );
예제 : ASSERT 매크로 #include <stdio.h> #define ASSERT(exp) { if (!(exp)) \ { printf(" 가정 (" #exp ") 이소스파일 %s %d 번째줄에서실패.\n"\, FILE, LINE ), exit(1);}} int main(void) { int sum; // 지역변수의초기값은 0 이아님 매크로를다음줄로연장할때사용 } ASSERT(sum == 0); // sum 의값은 0 이되어야함. return 0; 가정 (sum == 0) 이소스파일 c:\source\chapter15\macro3\macro3\macro3.c 11 번째줄에서실패.
비트관련매크로 매크로들은변수를받아서특정비트값을반환하거나설정한다. GET_BIT() 는변수 w 에서 k 번째비트의값을 0 또는 1 로반환한다. #define GET_BIT(w, k) (((w) >> (k)) & 0x01) SET_BIT_ON() 는변수 w 의 k 번째비트를 1 로설정하는매크로이다. #define SET_BIT_ON(w, k) ((w) = (0x01 << (k))) SET_BIT_OFF() 는변수w의k번째비트를 0로설정하는매크로이다. #define SET_BIT_OFF(w, k) ((w) &= ~(0x01 << (k)))
예제 : ASSERT 매크로 #include <stdio.h> #define GET_BIT(w, k) (((w) >> (k)) & 0x01) #define SET_BIT_ON(w, k) ((w) = (0x01 << (k))) #define SET_BIT_OFF(w, k) ((w) &= ~(0x01 << (k))) int main(void) { int data=0; SET_BIT_ON(data, 2); printf("%08x\n", data); printf("%d\n", GET_BIT(data, 2)); } SET_BIT_OFF(data, 2); printf("%08x\n", data); printf("%d\n", GET_BIT(data, 2)); return 0; 00000004 1 00000000 0
중간점검 1. 함수매크로와함수중에서속도면에서유리한것은? 2. 주어진수의 3 제곱을수행하는함수매크로를정의하여보자.
#ifdef 어떤조건이만족되었을경우에만컴파일하는조건부컴파일지시 #ifdef 매크로문장1 #else 문장2 #endif // 매크로가정의되었을경우 // 매크로가정의되지않았을경우
#ifdef 의예
예제 #include <stdio.h> #define DELUXE int main(void) { #ifdef DELUXE printf(" 딜럭스버전입니다. \n"); #endif return 0; } 딜럭스버전입니다.
예제 #include <stdio.h> #define LINUX int main(void) { #ifdef LINUX... #else... #endif return 0; } LINUX 버전 WINDOWS 버전
Visual C++ 에서설정하는방법
#ifndef, #undef #ifndef 어떤매크로가정의되어있지않으면컴파일에포함된다. #undef 매크로의정의를취소한다
중간점검 1. 전처리기지시자 #ifdef 을사용하여 TEST 가정의되어있는경우에만화면에 TEST" 라고출력하는문장을작성하여보자.
#if 기호가참으로계산되면컴파일 조건은상수이어야하고논리, 관계연산자사용가능
#if-#else-#endif ( 예 ) #if NATION == 1 #include "korea.h" #elif NATION == 2 #include "china.h" #else #include "usa.h" #endif
다양한예 #if (VERSION > 3) // 가능! 버전이 3 이상이면컴파일... #endif #if (AUTHOR == KIM) // 가능!! KIM은다른매크로 #if (VERSION*10 > 500 && LEVEL == BASIC) // 가능!! #if (VERSION > 3.0) // 오류!! 버전번호는 300과같은정수로표시 #if (AUTHOR == "CHULSOO") // 오류!! #if (VERSION > 300 defined(deluxe) )
조건부컴파일을이용하는디버깅 #define DEBUG 1... #if DEBUG == 1 printf(" 현재 counter 의값은 %d 입니다.\n", counter); #endif
조건부컴파일을이용하는디버깅 #define DEBUG... #ifdef DEBUG printf(" 현재 counter 의값은 %d 입니다.\n", counter); #endif... #if defined(debug) printf(" 현재 counter 의값은 %d 입니다.\n", counter); #endif
다수의라인을주석처리 #if 0 // 여기서부터시작하여...... #endif // 여기까지주석처리된다.
예제 정렬알고리즘을선택 #define SORT_METHOD 3 #if (SORT_METHOD == 1)... // 선택정렬구현 #elif (SORT_METHOD == 2)... // 버블정렬구현 #else... // 퀵정렬구현 #endif
중간점검 1. #if를사용하여 DEBUG가 2일경우에만 DEBUG" 가나오도록문장을작성하라. 2. #if를사용하여 DEBUG가 2이고 LEVEL이 3인경우에만 DEBUG" 가나오도록문장을작성하라.
헤더파일이중포함방지 /*** *stdio.h - definitions/declarations for standard I/O routines ****/ #ifndef _INC_STDIO #define _INC_STDIO...... #endif 헤더파일이포함되면매크로가정의되어서이중포함을방지합니다.
다중소스파일 단일소스파일 파일의크기가너무커진다. 소스파일을다시사용하기가어려움 다중소스파일 서로관련된코드만을모아서하나의소스파일로할수있음 소스파일을재사용하기가간편함
다중소스파일
예제 multiple_source.c // 다중소스파일 #include <stdio.h> #include "power.h" int main(void) { int x,y; printf("x 의값을입력하시오 :"); scanf("%d", &x); printf("y 의값을입력하시오 :"); scanf("%d", &y); printf("%d 의 %d 제곱값은 %f\n", x, y, power(x, y)); power.h // power.c에대한헤더파일 #ifndef POWER_H #define POWER_H double power(int x, int y); #endif power.c // 다중소스파일 #include "power.h double power(int x, int y) { double result = 1.0; int i; } return 0; for(i = 0;i < y; i++) result *= x; } return result;
헤더파일을사용하지않으면
헤더파일을사용하면
다중소스파일에서외부변수
비주얼 C++ 에서다중소스파일
비주얼 C++ 에서다중소스파일
비주얼 C++ 에서다중소스파일
헤더파일이중포함방지 #include <stdio.h> #include "rect.h" #include "rect.h" 구조체의정의가이중으로포함되어서오류가발생한다. #define DEBUG void draw_rect(const RECT *r) { #ifdef DEBUG printf("draw_area(x=%d, y=%d, w=%d, h=%d) \n", r->x, r->y, r->w, r->h); #endif }
헤더파일이중포함방지 #ifndef RECT_H #define RECT_H struct rect { int x, y, w, h; }; RECT_H 가정의되어있지않은경우에만포함시킨다. RECT_H 매크로를정의한다. typedef struct rect RECT; void draw_rect(const RECT *); double calc_area(const RECT *); void move_rect(rect *, int, int); #endif
중간점검 1. 다음문장의참거짓을말하라. 여러소스파일을이용하는것보다하나의소스파일로만드는편이여러모로유리하다. 2. 팩토리얼을구하는함수가포함된소스파일과관련헤더파일을제작하여보자.
비트필드구조체 멤버가비트단위로나누어져있는구조체 struct 태그이름 { 자료형멤버이름 1: 비트수 ; 자료형멤버이름 2: 비트수 ;... }; struct product { unsigned style : 3; unsigned size : 2; unsigned color : 1; }; unsigned int color size style
bit_field.c // 비트필드구조체 #include <stdio.h> struct product { unsigned style : 3; unsigned size : 2; unsigned color : 1; }; style=5 size=3 color=1 sizeof(p1)=4 p1=ccccccfd int main(void) { struct product p1; p1.style = 5; p1.size = 3; p1.color = 1; printf("style=%d size=%d color=%d\n", p1.style, p1.size, p1.color); printf("sizeof(p1)=%d\n", sizeof(p1)); printf("p1=%x\n", p1); } return 0;
비트필드사용시에주의점 struct product { long code; // 1 일반멤버도가능하다. unsigned style : 3; unsigned : 5; // 2 자리만차지한다. unsigned size : 2; unsigned color : 1; unsigned : 0; // 3 현재워드의남아있는비트를버린다. unsigned state : 3; // 여기서부터는다음워드에서할당된다. }; 비트필드의응용분야 : 하드웨어포트제어
비트필드의장점 메모리가절약된다. ON 또는 OFF의상태만가지는변수를저장할때 32비트의 int 형변수를사용하는것보다는 1비트크기의비트필드를사용하는편이훨씬메모리를절약한다.
실습 : 전처리기사용하기 원의면적을구하는프로그램을미국버전과한국버전으로작성한다. 미국버전에서는모든메시지가영어로출력되고단위도인치가된다. 한국버전에서는모든메시지가한글로출력되고단위도 cm가된다. SQUARE() 함수매크로도억지로사용하여보자.
실행결과 Please enter radius of a circle(inch) : 100 area(100.000000) is called area of the circle is 31415.920000 exp=5 원의반지름을입력하시오 (cm): 100 area(100.000000) 가호출되었음원의면적은 31415.920000 입니다.
실습예제 #include <stdio.h> #define USA #define DEBUG #ifndef PI #define PI 3.141592 #endif #ifndef SQUARE #define SQUARE(r) (r)*(r) #endif double area(double radius) { double result=0.0; #ifdef DEBUG #ifdef USA printf("area(%f) is called \n", radius); #else printf("area(%f) 가호출되었음radius); #endif #endif result = PI*SQUARE(radius); return result; }
실습예제 int main(void){ double radius; #ifdef USA printf("please enter radius of a circle(inch) : "); #else printf(" 원의반지름을입력하시오 "); #endif scanf("%lf", &radius); #ifdef USA printf("area of the circle is %f \n", area(radius)); #else printf(" 원의면적은 %f입니다\n", area(radius)); #endif return 0; }
도전문제 버전을나타내는매크로를정의하고버전이 100 이하이면원의면적을계산할수없다는메시지를출력하고종료하게끔, 위의프로그램을수정하여보자. DATE 와 LINE 을출력하여보자.
중간점검 1. 구조체의일종으로멤버들의크기가비트단위로나누어져있는구조체는 이다. 2. 비트필드구조체를정의하는경우, 자료형은 이나 을사용하여야한다.
Q & A