C언어 (25) 조건부, 분할 컴파일
마지막으로 조건부, 분할 컴파일에 대한 내용이다.
이론적이라기 보다는 컴파일 방법에 대한 내용이니 간단히 적어도 될 것 같다.
조건부 컴파일
C언어는 다양한 OS에서 사용되었기에 제공하는 표준 함수나 동작이 조금씩 다를 수 있다.
같은 운영체제를 사용한다 하더라도 사용하는 컴파일러와 라이브러리가 다를 수도 있다.
그래서 소스 코드를 다르게 작성하면 나중에 유지 보수가 어려워진다.
이러한 문제를 해결하기 위해서 조건부 컴파일을 사용한다.
특정 조건에 만족할 때만 코드가 컴파일 되게 하고, 보통 매크로 상수를 검사하고 수행한다.
조건에 따라 특정 코드를 컴파일 하도록 만들 수 있어 이식성 있는 코드를 개발할 수 있다.
전처리기 지시자 | 설명 |
---|---|
#include | 헤더 파일을 포함할 때 사용. |
#define | 상수나 매크로를 정의하고 코드 내에서 지정한 이름으로 값을 치환. |
#undef | 이미 정의된 매크로를 해제 |
#if , #elif , #else , #endif | 조건에 따라 컴파일 |
#ifdef | 매크로가 정의되어 있는지 확인. 정의되어 있으면 코드 블록을 컴파일. |
#ifndef | 매크로가 정의되어 있지 않으면 코드 블록을 컴파일. |
#if부터 #ifndef까지 알아보자.
#if~#endif
어떤 조건에 만족하는지를 검사하여 컴파일 할 문장을 #if~#endif로 묶어준다.
#if 문의 조건식에는 매크로 상수를 비교하는 산술, 관계, 논리 연산자 등을 사용할 수 있다.
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
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define CAL 2
int main() {
double num1, num2, result;
printf("실수 두 개를 입력하세요 : ");
scanf("%lf %lf", &num1, &num2);
#if(CAL==1)
result = num1 / num2;
printf("나누기 : %lf", result);
#endif
#if(CAL==2)
result = num1 * num2;
printf("곱하기 : %lf", result);
#endif
#if(CAL==3)
result = num1 + num2;
printf("더하기 : %lf", result);
#endif
#if(CAL==4)
result = num1 - num2;
printf("빼기 : %lf", result);
#endif
return 0;
}
1
2
실수 두 개를 입력하세요 : 2.3 3.4
곱하기 : 7.820000
코드로 보면 어떤 의미인지 바로 이해가 될 것이다.
CAL의 값에 따라 #if 문이 참이라면 포함하는 문장을 컴파일 한다.
CAL의 숫자를 바꾸면 당연하게 다른 부분을 선택해서 컴파일 할 수 있다.
#if, #elif와 #else는 조건문의 if, else if, else와 같은 관계를 가진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define CAL 2
int main() {
double num1, num2, result;
printf("실수 두 개를 입력하세요 : ");
scanf("%lf %lf", &num1, &num2);
#if(CAL==1)
result = num1 / num2;
printf("나누기 : %lf", result);
#elif(CAL==2)
result = num1 * num2;
printf("곱하기 : %lf", result);
#elif(CAL==3)
result = num1 + num2;
printf("더하기 : %lf", result);
#else(CAL==4)
result = num1 - num2;
printf("빼기 : %lf", result);
#endif
return 0;
}
이렇게 사용이 된다. if~elif~else는 한 구문으로 이어지기에 #endif가 마지막에만 들어간다.
#ifdef, #ifndef
#ifdef는 if defined의 줄임말로 뒤에 지정한 매크로 이름이 선언되어 있으면 컴파일을 한다.
반대로 #ifnedf는 뒤에 지정한 캐므로 이름이 선언되어 있지 않으면 컴파일을 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#define ADD 1
int main() {
double num1=1.1, num2=2.2, result=0;
#ifdef ADD
result = num1 + num2;
printf("더하기 : %lf\n", result);
#endif
#ifndef ADD
printf("더하기를 할 수 없습니다.\n");
#endif
#ifdef MUL
result = num1 * num2;
printf("더하기 : %lf\n", result);
#endif
#ifndef MUL
printf("곱하기를 할 수 없습니다.\n");
#endif
return 0;
}
1
2
더하기 : 3.300000
곱하기를 할 수 없습니다.
역시 코드를 보면 쉽게 이해가 될 것이다.
매크로가 있으면 #ifdef에서 컴파일되고, 매크로가 없으면 #ifndef 구문이 컴파일된다.
이 #ifdef와 #ifndef는 밑에서 볼 파일 분할 컴파일에서 유용하게 쓸 수 있다.
파일 분할 컴파일
이제까지 해본 예제들은 오직 하나의 소스 파일만을 컴파일하고 실행을 했다.
그런데 하나의 소스 파일로 개발하면 프로그램의 내용이 복잡해지고 관리의 어려움이 생긴다.
문제점을 개선하기 위해 소스 파일을 여러 개로 분할하여 프로그램을 개발하는 것이 좋다.
자주 사용하는 방법으로는 사용자 헤더 파일을 만들어 전역 변수, 구조체, 함수 등을 모아 놓는다.
그러면 그런 것들을 메인 소스 코드와 다른 파일로 관리하기에 가독성도 좋고 유지 보수에 좋다.
이렇게 main함수가 있는 file.c와 my_header.c, my_header.h를 같은 경로에 만든다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//myheader.h
double area(int r);
double circle(int r);
//myheader.c
#define PI 3.14
double circle(int r) {
double result = 2 * PI * r;
return result;
}
double area(int r) {
double result = PI * r * r;
return result;
}
//file.c
#include <stdio.h>
#include "my_header.h"
int main() {
printf("원의 둘레 : %lf, 원의 넓이 : %lf", circle(5), area(5));
return 0;
}
1
원의 둘레 : 31.400000, 원의 넓이 : 78.500000
그 다음 이렇게 3개를 나눠서 소스 코드를 적는다.
훨씬 가독성이 좋고, 문제가 생겼을 때 문제가 생긴 파일만 보면 되기에 관리도 훨씬 편해졌다.
사용자 헤더 파일을 include 할 때는 “” 큰따옴표로 묶어 줘야 한다.
그러면 어떻게 #ifndef를 파일 분할 컴파일에 이용하는지 보고 끝내자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// file.c
#include <stdio.h>
#include "my_header1.h"
#include "my_header2.h"
int main() {
printf("%d", HIHI);
return 0;
}
//my_header1.c
#define HIHI 3
//my_header2.c
#define HIHI 4
이렇게 my_header1과 2에 둘다 HIHI라는 매크로를 정의하면 어떤 문제가 있을까?
뒤에 선언한 매크로가 처리되며 매크로의 값이 겹치면 의도하지 않은 결과가 발생할 수 있다.
이 때 #ifndef를 이용하면 중복된 매크로의 처리를 쉽게 해서 안정성을 높일 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// file.c
#include <stdio.h>
#include "my_header1.h"
#include "my_header2.h"
int main() {
printf("%d", HIHI);
return 0;
}
//my_header1.c
#ifndef HIHI
#define HIHI 3
#endif
//my_header2.c
#ifndef HIHI
#define HIHI 4
#endif
이렇게 header1.h가 include 되면 HIHI가 아직 없으므로 3으로 매크로를 선언하지만
header2.h가 include 될 때는 이미 HIHI가 있으므로 컴파일이 되지 않아 값이 덮이지 않는다.
대규모 프로젝트를 만들다면 유용하게 사용할 수 있을 것 같다.
이것으로 C언어 책 한권을 다 정리했다.
마지막으로 visual studio랑 오류들 조금 적고 마무리하면 될 것 같다.