340 likes | 742 Views
쉽게 풀어쓴 C 언어 Express. 제 15 장 전처리 및 비트 필드. C Express. 이번 장에서 학습할 내용. 전처리와 기타 중요한 테마에 대하여 학습한다. 전처리 지시자 다중 소스 프로그램 비트 필드. 전처리기란 ?. 전처리기 (preprocessor ) : 컴파일에 앞서 소스 파일을 처리. 수고했어 , 나머지는 나한테 맡겨 !. #include, #define 등을 처리 합니다. 소스 파일. 임시 파일. 오브젝트 파일. 컴파일 러. 전처리기. 단순 매크로.
E N D
쉽게 풀어쓴 C언어 Express 제15장 전처리 및 비트 필드 C Express
이번 장에서 학습할 내용 전처리와 기타 중요한 테마에 대하여 학습한다. • 전처리 지시자 • 다중 소스 프로그램 • 비트 필드
전처리기란? • 전처리기(preprocessor): 컴파일에 앞서 소스 파일을 처리 수고했어, 나머지는 나한테 맡겨! #include, #define 등을 처리 합니다. 소스 파일 임시 파일 오브젝트 파일 컴파일러 전처리기
단순 매크로 사람은 숫자보다기호를 잘 기억하므로 3.141592보다는 PI가 낫죠! • 단순 매크로(macro): 기호 상수 정의 #define SIZE 100 #define PI 3.141592 #define SIZE 100 while (i < SIZE) { sum += i; i++; } while (i < 100) { sum += i; i++; } 전처리기
단순 매크로의 장점 • 프로그램의 가독성향상 • 상수 변경이 용이 리터럴을사용하는 경우: 모든 곳을 수정해야 함 기호 상수를 사용하는 경우: 정의된 부분만 수정하면 됨
단순 매크로의 예 #define SIZE 100 // 배열 크기 #define MAX_INT 2147483647 // 최대정수 #define PI 3.141592// 원주율 #define TWOPI (3.141592 * 2.0) // 원주율의 2배 #define EOF (-1) // 파일의 끝 표시 #define DIGITS "0123456789"// 문자 상수 정의 #define BRACKET "(){}[]"// 문자 상수 정의 #define MAX_LEN 100 // 문자열 최대 길이 #define MAX_SIZE (MAX_LEN + 1) // 문자 배열 최대 크기 #define EXPR x = y + 1; p = "abc"// 어떤 형태라도 정의 가능
함수형 매크로 • 함수형 매크로: 함수처럼 인자를 가질 수 있는 매크로 • #define SQUARE(x) ((x) * (x)) #define SUM(x, y) ( (x) + (y) ) #define AVERAGE(x, y, z) ( ( (x) + (y) + (z) ) / 3 ) #define MAX(x, y) ( (x) > (y) ) ? (x) : (y) ) #define ABS(x) ( (x) < 0 ) ? -(x) : (x) ) • #define getchar() getc(stdin) • #define putchar(c) putc((c), stdout) v = SQUARE(3); v = ((3) * (3));
주의할 점 #define SQUARE(x) x * x// 위험 !! v = SQUARE(1 + 2); // v = 1 + 2 * 1 + 2; 1 + 2 + 2 5 (≠ 9) #define SQUARE(x) (x) * (x)// 위험 !! v = SQUARE(1 + 2); // v = (1 + 2) * (1 + 2); 3 * 3 9 v = 20 / SQUARE(2); // v = 20 / (2) * (2); 10 * 2 20 (≠ 5) #define SQUARE(x) ((x) * (x))// 최대한 안전한 형태 v = SQUARE(1 + 2); // v = ((1 + 2) * (1 + 2)); (3 * 3) 9 v = 20 / SQUARE(2); // v = 20 / ((2) * (2)); 20 / 4 5 함수형 매크로에서는 인자와 전체를 괄호로 둘러싸는 것이 안전합니다.
함수형 매크로의 장단점 • 함수 호출보다 실행 속도가 빠름 • 큰 매크로를 많이 사용하면 실행 프로그램 크기 증가 • 디버깅이 어려움 • 함수에 비해 위험 #define SQUARE(x) ((x) * (x)) n = 1; v = SQUARE(++n); // v = ((++n) * (++n)); // n = 3, v = 9 or 6 intsquare(int x) { return x * x; } n = 1; v = square(++n); // n = 2, v = 4
예제 #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)); // 논리오류 return 0; } 4 9 1.440000 25 25 16
매크로 정의 해제 • #undef매크로:매크로에대한 정의 취소(undefine) • #define SIZE 10 • … • #undefSIZE • intSIZE; • 긴 매크로 처리 방법: 한 줄 이상이면 반드시 끝에 연속 마크(\) 사용 #define OUT_ERROR(code, message) \ printf("[%d] %s\n", \ (code), (message))
매크로 연산자 • #연산자: 매크로 인자를 문자열로 변환 (전위 단항 연산자) #define PRINT(exp) printf(#exp " = %d\n", (exp)) x = 1; PRINT(x + 2); // printf("x + 2" " = %d\n", x + 2); // printf("x + 2 = %d\n", x + 2); // x + 2 = 3 • ##연산자: 하나의 토큰으로 병합 (이항 연산자) #define VAR(n) v##n #define TOKEN(name, n) name ## n VAR(1) = 2; // v1 = 2; s = TOKEN(sum, 2); // s = sum2;
예제 #include<stdio.h> #define MAKE_NAME(n) v ## n #define PRINT(n) printf("v" #n " = %d\n", MAKE_NAME(n)); intmain(void) { int MAKE_NAME(1) = 10; // int v1 = 10; int MAKE_NAME(2) = 20; // int v2 = 20; PRINT(1); // printf("v1 = %d\n", v1); PRINT(2); // printf("v2 = %d\n", v2); return 0; } v1 = 10 v2 = 20
내장 매크로 • 내장 매크로: 미리 정의된 매크로 printf("컴파일 시간 = %s %s\n", __DATE__, __TIME__); // 컴파일 시간 = Oct 9 2012 13:03:04 printf("에러 발생: 파일 이름 = %s, 라인 번호 = %d\n", __FILE__, __LINE__);
예제: ASSERT 매크로 #include<stdio.h> #include<stdlib.h> #define ASSERT(exp) \ if (!(exp))\ printf("[%s %d] 조건 실패: " #exp "\n", __FILE__, __LINE__), exit(1) int main(void) { int sum; ASSERT(sum == 0); return 0; } [assert.c 10] 조건 실패: sum == 0
비트 조작 매크로 • #defineBIT_GET(w, k) (((w) >> (k)) & 0x01) • w에서 k번째 비트 값획득 • 결과: 0 / 1 • #defineBIT_SET (w, k) ((w) | 0x01 << (k)) • w의 k번째 비트를1로 설정 • #defineBIT_RESET(w, k) ((w) & ~(0x01 << (k))) • w의 k번째 비트를0로 설정
예제: 비트 조작 매크로 #include<stdio.h> #defineBIT_GET(w, k) (((w) >> (k)) & 0x01) #defineBIT_SET(w, k) ((w) | 0x01 << (k)) #defineBIT_RESET(w, k) ((w) & ~(0x01 << (k))) int main(void) { intdata = 0; data = BIT_SET(data, 2); printf("%08X %d\n", data, BIT_GET(data, 2)); data = BIT_RESET(data, 2); printf("%08X %d\n", data, BIT_GET(data, 2)); return 0; } 00000004 1 00000000 0
조건부 컴파일 지시자 • #if조건: 조건이 참이면 • 조건: 정수 상수 조건이어야 하고 논리, 관계 연산자 등 허용 • #ifdef매크로: 매크로가 정의되어 있으면 • #ifdefM #ifdefined(M) • defined(M): 전처리 연산자, 매크로 M이 정의되어 있으면 참 • #ifndef매크로: 매크로가 정의되어 있지 않으면 • #ifndefM #if!defined(M) • #else: 그렇지 않으면 • #elif조건: 그렇지 않고 조건이 참이면 (else if) • #endif: 조건부 컴파일 지시자 끝 • #if / #ifdef/ #ifndef …#endif
조건부 컴파일 지시자 예 #if DEBUG_LEVEL >= 1 printf("result = %d\n", result); #endif #ifdefLINUX ... // LINUX 버전인 경우 #else ... // LINUX 버전이아닌 경우 #endif #if NATION == 1 #include"korea.h" #elif NATION == 2 #include"china.h" #else #include"usa.h" #endif #ifndef LIMIT // LIMIT이 정의되어 있지 않으면 # define LIMIT 1000 #endif #if 0 // 전처리 기능을 이용한 주석 처리 … … #endif
예제 #include<stdio.h> #defineDELUXE int main(void) { #ifdef DELUXE printf("딜럭스 버전입니다.\n"); #endif return0; } 딜럭스 버전입니다.
조건부 컴파일 지시자 • #if VERSION > 3// 가능! 버전이 3보다 크면 • #if(AUTHOR == KIM)// 가능! KIM은 매크로 • #if(VERSION * 10 > 500 && LEVEL == BASIC)// 가능! • #if(VERSION > 3.0) // 오류! 실수는 허용되지 않음 • #if(AUTHOR == "CHULSOO") // 오류! 문자열은 허용되지 않음 • #if(VERSION > 300 || defined(DELUXE))// 가능!
#include • #include <…> • 표준디렉토리(폴더)에서 파일 탐색 • #include "…" • 현재 디렉토리에서 파일 탐색 표준디렉토리에서 파일 탐색 (현재 디렉토리에 없는 경우) • 경로 지정 가능 • #include "graphic/point.h" • #include "D:\Project\source\point.h" • #include "/home/user1/source/point.h"
헤더 파일 중복 포함 방지 // rect.h #ifndef RECT_H #define RECT_H structrect{ intx, y, w, h; }; … #endif // rect.h structrect{ intx, y, w, h; }; … // shape.h #include"rect.h" … // shape.h #ifndef SHAPE_H #define SHAPE_H #include"rect.h" … #endif // main.c #include<stdio.h> #include"rect.h" #include"shape.h" …
다중 소스 파일 • 단일 소스 파일 • 소스의 재사용과 관리가 어려움 • 일부만 수정한 경우에도전체를 다시 컴파일해야 함 • 다중 소스 파일 • 모듈별로 별도의 소스 파일 유지 소스의 재사용과 관리가 용이함 • 변경된 소스 파일만 다시 컴파일 컴파일 시간 단축
다중 소스 파일 라이브러리
예제 power.h main.c #ifndef POWER_H #define POWER_H doublepower(int x, int y); #endif #include<stdio.h> #include"power.h" int main(void) { intx,y; printf("x의값을입력하시오: "); scanf("%d", &x); printf("y의값을입력하시오: "); scanf("%d", &y); printf("%d의 %d 제곱값은 %f\n", x, y, power(x, y)); return 0; } power.c #include"power.h" double power(int x, int y) { double result = 1.0; inti; for (i = 0;i < y; i++) result *= x; return result; } x의 값을 입력하시오: 2 y의 값을 입력하시오: 3 2의 3 제곱값은8.000000
헤더 파일을 사용하지 않으면 함수 원형 선언 반복
헤더 파일을 사용하면 헤더 파일 포함
비트 필드 • int/unsigned타입의 구조체필드는 비트 단위로 크기 지정 가능 비트 필드 • 메모리 절약 struct product { unsigned style : 1; // [0, 1] unsigned size : 2; // [0, 3] int value : 5; // [-16, +15] }; int/unsigned (word) value size style
예제 #include<stdio.h> struct product { unsigned style : 1; unsigned size : 2; int value : 5; }; int main(void) { struct product p; p.style= 1; p.size= 2; p.value= -10; printf("style = %u, size = %u, value = %d\n", p.style, p.size, p.value); printf("sizeof p = %d\n", sizeofp); printf("p = %08X\n", p); return 0; } value size style style = 1, size = 2, value = -10 sizeof p = 4 p = 000000B5
비트 필드 struct product { intcode;// 일반 멤버 unsigned style : 1; unsigned : 3; // 자리만 차지 unsigned size : 2; int value : 5; unsigned : 0; // 현재 워드의 나머지 비트는 사용하지 않음 unsigned state : 4; // 다음 워드에할당 }; value size style state