개정판 누구나 즐기는 C언어 콘서트 제14장 전처리와 분할 컴파일리 출처: pixabay
이번 장에서 학습할 내용 전처리와 기타 중요한 테마에 대하여 학습한다. 전처리 지시어 분할 컴파일 명령어 라인의 매개변수
이번 장에서 만들 프로그램
전처리기란? 전처리기 (preprocessor)는 컴파일하기에 앞서서 소스 파일을 처리 하는 컴파일러의 한 부분
전처리기의 요약
단순 매크로
단순 매크로의 동작
단순 매크로의 장점 프로그램의 가독성을 높인다. 상수의 변경이 용이하다. 기호 상수를 사용하는 경우 숫자를 사용하는 경우
중간 점검 1. 숫자 상수를 기호 상수로 정의하는 데 사용되는 전처리 지시자는 #________이다. 2. #define을 이용하여서 하나의 텍스트를 다른 텍스트로 정의할 수 있는가?
Lab: &&를 and로 바꾸기 어느 학교에서 학생이 졸업하려면 120학점을 따야하고 성적평균이 2.0이상이어야 한다고 하자. 학점과 성적평균을 물어봐서 졸업여부 를 판정하는 프로그램을 작성하자. 단 &&나 || 대신에 and와 or를 사용한다.
Sol: #include <stdio.h> #define and && #define or || int main(void) { int credits; double gpa; printf("지금까지 획득한 학점수를 입력하시오: "); scanf("%d", &credits); printf("지금까지 획득한 학점평균을 입력하시오: "); scanf("%lf", &gpa); if (credits >= 120 and gpa >= 2.0) printf("졸업 가능합니다. \n"); else printf("좀 더 다녀야 합니다. \n"); return 0; }
함수 매크로
함수 매크로의 예 #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)
주의할 점
예제 #include <stdio.h> #define SQUARE(x) ((x) * (x)) int main(void) { int x = 2; printf("SQUARE(3) = %d\n", SQUARE(3)); // 상수에도 적용 가능 printf("SQUARE(1.2) = %f\n", SQUARE(1.2)); // 실수에도 적용 가능 printf("SQUARE(2+3) = %d\n", SQUARE(2 + 3)); // 수식에도 적용 가능 printf("x = %d\n", x); // 변수에도 적용 가능 printf("SQUARE(x) = %d\n", SQUARE(x)); // 변수에도 적용 가능 printf("SQUARE(++x) = %d\n", SQUARE(++x)); // 논리 오류 return 0; }
함수 매크로의 장단점 함수 매크로의 장단점 함수 호출 단계가 필요없어 실행 속도가 빠르다. 소스 코드의 길이가 길어진다. 간단한 기능은 매크로를 사용 #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define ABS(x) ((x) > 0 ? (x) : -(x))
중간 점검 1. 함수 매크로는 함수보다 속도가 느린가? 2. 3제곱을 수행하는 함수 매크로를 정의하여 보자.
Lab: 비트 조작하기 매크로를 테스트하는 프로그램을 작성하라. #define SETBIT(x, n) ((x) |= (1<<(n))) #define CLEARBIT(x, n) ((x) &= ~(1<<(n))) #define TESTBIT(x, n) ((x) & (1<<(n)))
Sol: // 매크로 예제 #include <stdio.h> #define SETBIT(x, n) ((x) |= (1<<(n))) #define CLEARBIT(x, n) ((x) &= ~(1<<(n))) #define TESTBIT(x, n) ((x) & (1<<(n))) int main(void) { int x = 0x1011; printf("SETBIT(x, 8)전 변수 x = %x\n", x); SETBIT(x, 8); printf("SETBIT(x, 8)후 변수 x = %x\n", x); printf("CLEARBIT(x, 8)전 변수 x = %x\n", x); CLEARBIT(x, 8); printf("CLEARBIT(x, 8)후 변수 x = %x\n", x); return 0; }
#ifdef
예
예제 어떤 회사에서 리눅스와 윈도우즈 버전의 프로그램을 개발하였다고 하자.
Sol: #include <stdio.h> #define LINUX int main(void) { #ifdef LINUX printf("리눅스 버전입니다. \n"); #else printf("윈도우 버전입니다. \n"); #endif return 0; }
Lab: 함수 매크로의 사용 PI는 단순 매크로로 정의한다. 원의 면적을 계산하는 매크로 CIRCLE_AREA(r)를 정의해보자. DEBUG를 정의한다. DEBUG가 정의되어 있으면 “디버깅 모드입니다.”를 화면에 출력한 다.
Sol: #include <stdio.h> #define DEBUG #define PI 3.1415 #define CIRCLE_AREA(r) (PI*r*r) int main(void) { double radius, area; #ifdef DEBUG printf("“디버깅 모드입니다.\n"); #endif printf("원의 반지름: "); scanf("%lf", &radius); area = CIRCLE_AREA(radius); printf("원의 면적=%.2f", area); return 0; }
중간 점검 1. 전처리기 지시자 #ifdef을 사용하여 TEST가 정의되어 있는 경우에만 화면에 “TEST"라고 출력하는 문장을 작성하여 보자.
#if 기호가 참으로 계산되면 컴파일 조건은 상수이어야 하고 논리, 관계 연산자 사용 가능 #if (DEBUG == 1) // DEBUG 값이 1이면 컴파일 printf("value=%d\n", value); #endif #if (VERSION > 3) // 버전이 3 이상이면 컴파일 ... #if 0 // 여기서부터 시작하여 void test() { /* 여기에 주석이 있다면 코드 전체를 주석 처리하는 것이 쉽지 않다. */ sub(); } #endif // 여기까지 주석 처리된다.
예제 #1 #define NATION 1 #if NATION == 1 printf("안녕하세요?"); #elif NATION == 2 printf("你好吗?"); #else printf("Hello World!"); #endif
Lab: 헤더 파일 중복 포함 막기 실수로 헤더 파일이 중복하여 소스 파일에 포함되면 예기치 못한 컴 파일 오류를 발생시킨다. 이것을 막기 위하여 #ifndef 지시어를 사 용할 수 있다. struct STUDENT { int number; char name[10]; }; #include "student.h" #include "student.h" // 실수로 2번 포함시켰다! int main(void) { return 0; }
Sol: #ifndef STUDENT_H #define STUDENT_H struct STUDENT { int number; char name[10]; }; #endif
다중 소스 파일 단일 소스 파일 파일의 크기가 너무 커진다. 소스 파일을 다시 사용하기가 어려움 다중 소스 파일 서로 관련된 코드만을 모아서 하나의 소스 파일로 할 수 있음 소스 파일을 재사용하기가 간편함
다중 소스 파일
예제 9의 제곱을 계산하여 화면에 출력하는 다음과 같은 프로그램을 작 성해보자.
다중 소스 파일 사용
예제 power.h // power.c에 대한 헤더 파일 #ifndef POWER_H main.c #define POWER_H double power(int x, int y); #endif main.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)); return 0; } power.c // 다중 소스 파일 #include "power.h“ double power(int x, int y) { double result = 1.0; int i; for(i = 0;i < y; i++) result *= x; return result; }
헤더 파일을 사용하지 않으면
헤더 파일을 사용하면
다중 소스 파일에서 외부 변수
중간 점검 다음 문장의 참 거짓을 말하라. “여러 소스 파일을 이용하는 것보다 하나의 소스 파일로 만드는 편이 여러모로 유리하다.” 팩토리얼을 구하는 함수가 포함된 소스 파일과 관련 헤더 파일을 제작하여 보자.
main() 함수의 인수 지금까지의 main() 함수 형태 외부로부터 입력을 받는 main() 함수 형태 int main(void) { .. } int main(int argc, char *argv[]) { .. }
인수 전달 방법 C: \cprogram> mycopy src dst
예제 #include <stdio.h> int main(int argc, char *argv[]) { int i = 0; for(i = 0;i < argc; i++) printf("명령어 라인에서 %d번째 문자열 = %s\n", i, argv[i]); return 0; } c:\cprogram\mainarg\Debug>mainarg src dst 명령어 라인에서 0번째 문자열 = mainarg 명령어 라인에서 1번째 문자열 = src 명령어 라인에서 2번째 문자열 = dst c:\cprogram\mainarg\Debug>
중간 점검 main()의 argv[0]에는 무엇이 들어 있는가? “C>test a b c” 라고 도스 창에서 입력하였다면 argc, arv[]에는 어떤 값들이 들어 가는가?
Q & A
감사합니다.