전처리기 구문은 다른 프로그램 영역과 독립적으로 처리되고 소스코드 파일 단위로 효력이 존재한다.
파일 포함 전처리기
#include는 전처리기에서 가장 많이 사용되는 문법이고 특정한 파일을 라이브러리로서 포함시키기 위해 사용한다.
#include 구문으로 가져올 수 있는 파일에는 제약이 없다.
즉, 어떤 파일이던 현재 프로그램의 소스코드로 가져올 수 있는 것이다.
구문 자체를 그 파일에 적혀있떤 소스코드 자체로 대체할 수 있다.
#include <파일이름> 과 같이 선언하면 시스템 디렉토리에서 파일을 검색한다.
운영체제마다 시스템 디렉토리가 존재하는 경로가 다를 수 있으며 대표적으로 stdio.h와 같은 헤더파일등이
시스템 디렉토리에 존재한다.
#include "파일이름" 과 같이 선언하면 현재폴더에서 파일을 먼저 검색한다.
만약 현재 폴더에 파일이 없다면 시스템 디렉토리에서 파일을 검색한다.
나만의 헤더를 만드는 방법
visual studio에서 소스코드를 생성할때와 마찬가지로 헤더파일을 생성할 수 있다.
프로젝트 내 소스파일 - 추가 - 새항목 - 헤더파일
000.h로 만들어 주면 #pragma once라는 visual studio에서 제공해주는 일종의 전처리 구문이 들어가게 되는데
이것은 C보다는 C++에 가까운 요소이다.
#pragma once를 지우고
int add(int a, int b){
return a + b;
}
이렇게 작성해준다. 헤더파일은 temp.h로 만들어 주었기 때문에 main.c에서 #include "temp.h" 로 선언을 해준뒤
메인 함수 내에서
printf("%d\n", add(3, 7)); 형태로 출력해주게 되면 temp.h에 적어둔 add에서 연산처리를 하여
10이라는 결과값을 출력하게 된다.
헤더 파일을 사용할때는 같은 이름의 함수가 중첩되지 않게 주의해야 한다.
예를들어 위와같은 헤더파일을 만들고 main.c 내에서 또다시 add함수로 만들어서 사용한다면 겹치기 때문에
조심해야 한다.
#include "temp.h"로 선언을 하고 아래에 또 add함수를 만든다는 것은
int add(int a, int b){ //temp.h
return a + b;
}
int add(int a, int b){//main.c 에서 새로 작성
return a + b;
}
int main(void){
printf("%d\n", add(3, 7));
}
이런 형태의 코드를 작성한것이나 마찬가지인 게 되는것이다.
매크로 전처리기
프로그램내에서 사용되는 상수나 함수를 매크로 형태로 저장하기 위해 사용하며
#define 문법을 사용해 정의할 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define PI 3.1415926535
int main(void){
int r = 10;
printf("원의 둘레 : %.2f\n", 2 * PI * r);
system("pause");
return 0;
}
위 코드에서 #define PI 3.1415926535 가 매크로 전처리기다.
메인함수에서 보면 출력문에서 2 * PI * r의 연산을 출력하도록 되는데 이 연산은 결국
2 * 3.1415926535 * 10 이 되는것이다.
전역변수처럼 PI라는 이름으로 불러다가 편하게 사용할 수 있는 것이다.
함수 형태로 사용될 수 있도록 하기 위해서 인자가 포함 될 수도 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define POW(x) (x * x)
int main(void){
int x = 10;
printf("x의 제곱 : %d\n", POW(x));
system("pause");
return 0;
}
제곱값을 구하기 위해 POW함수를 사용하였고 x로 값을 받아서 사용했다.
POW함수는 제곱을 계산하는 함수이며 x의 값인 10의 제곱을 연산해 100이라는 값을 출력한다.
처음 인강을 보면서는 함수라길래 무언가 했지만 수학관련함수같은것을 뜻하는 것 같다.
#define은 점점 배워갈수록 더 많이 사용하게 될것이며 소스코드의 양을 획기적으로 줄일 수 있다고 한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define ll long long
#define ld long double
int main(void){
ll a = 98654321000;
ld b = 100.5054;
printf("%.1f\n", a * b);
system("pause");
return 0;
}
이런 형태로 많이 사용한다고 한다.
만약 이런 매크로 전처리기를 사용하지 않았다면
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void){
long long a = 98654321000;
long double b = 100.5054;
printf("%.1f\n", a * b);
system("pause");
return 0;
}
이런형태로 작성해야 했을 것이다. 몇글자 차이이긴 하지만 만약 long long과 long double을 많이 사용하는
장문의 코드였다면 짧게 사용할 수 있는게 더 깔끔해보이고 개발자 입장에서도 더 편할 수 있게 된다.
조건부 컴파일
조건부컴파일은 컴파일이 이루어지는 영역을 직접 매크로 형태로 지정할 수 있는 기법이다.
일반적으로 디버깅과 소스코드 이식을 목적으로 하여 작성되고 C언어로 시스템 프로그램을 작성할 때에는
운영체제에 따라서 소스코드가 달라질 수 있다.
이때 운영체제에 따라서 컴파일이 수행되는 소스코드를 다르게 할 수 있다.
즉, 운영체제에 맞는 컴파일 구문을 넣어주는 방법으로 코딩을 해야할 때가 있는데 그럴 때 조건부 컴파일을
이용해서 어떤 상황에서는 어떤 컴파일을 할것인지 상황에 따른 컴파일 방법을 달리할 때 사용한다.
#ifndef ~ #endif 문법은 대표적인 조건부 컴파일 문법이다. 흔히 헤더파일의 내용이 중복되어 사용되지 않도록
하기 위해 적용한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "temp.h"
#include "temp.h"
int main(void){
system("pause");
return 0;
}
위와 같이 같은 헤더파일을 여러번 불러오게 되면 오류가 발생한다.
이렇게 여러번 불러오더라도 단 한번만 불러오도록 처리할 수 있는 것이 ifndef ~ endif 구문이다.
#ifndef _TEMP_H_
#define _TEMP_H_
int add(int a, int b){
return a + b;
}
#endif
temp.h를 위와같이 변경해주면 된다.
#ifndef _TEMP_H_는 temp.h파일이 정의가 되어있지 않다면 #endif까지의 아래 코드를 처리한다는 것이다.
그리고 ifndef안에서 #define _TEMP_H_로 정의하도록 하면 된다.
ifndef가 정의되어 있지 않다면 이라는 의미이기 때문에 한번이라도 temp.h가 정의되었다면 그 다음부터는
if문 안에 있는게 처리될 수 없기 때문에 중첩되서 오류가 발생하는것이 방지되는 것이다.
파일분할 컴파일
일반적으로 직접 라이브러리를 만들때에는 c언어 파일과 헤더파일을 모두 작성해야 한다.
main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "temp.h"
int main(void){
printf("%d\n", add(3, 7));
system("pause");
return 0;
}
temp.h
#ifndef _TEMP_H_
#define _TEMP_H_
int add(int a, int b);
#endif
temp.c
#include "temp.h"
int add(int a, int b){
return a + b;
}
위와 같이 나눠서 작성한다.
C파일로 만들때는 헤더파일과 동일한 파일명을 갖고 있어야 하며 헤더파일은 기본적인 선언만 하고 반환과
연산처리는 C파일에서 해주면 된다. 그리고 C파일에는 헤더파일을 정의한다는 의미에서
#include "temp.h"로 불러와줘야 한다.
레퍼런스
패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직