제 29강) 소스파일의 분할(헤더파일) |
오늘은 소스파일을 분할하여 편집하기 편하게, 보기 편하게 만드는 법을 알려드리려합니다.
(즉, 헤더파일이라는 것을 알게됩니다.)
헤더파일 |
소스파일을 분할시키기 전에 헤더파일에 대해서 먼저 알아봅시다.
헤더파일은 특히 C와 C++에서 컴파일러에 의해 다른 소스파일에 자동으로 포함된 소스코드의 파일을 뜻합니다.
즉, 무언가가 선언되어 있는 파일을 뜻하고 이것을 우리가 사용하기 위해 #include를 합니다.
헤더파일에는 주로 형을 알리는 것들이 들어가있습니다.
1. #define 2. enum (다음 강의에서 배웁니다.)
3. struct 선언부
4. class 선언부 (C++에 해당됩니다.)
5. 함수형
|
이런 것들이 선언되어 있습니다.
소스파일 분할하기 |
먼저 간단한 계산기 프로그램을 이용합시다.
#include <stdio.h>
int add(int num1, int num2)
{
return num1 + num2;
}
int min(int num1, int num2)
{
return num1 - num2;
}
int multi(int num1, int num2)
{
return num1 * num2;
}
int div(int num1, int num2)
{
if (num2 != 0)
{
return num1 / num2;
}
else
{
return 0;
}
}
int main()
{
int num1 = 15, num2 = 12;
printf("%-5d + %5d = %5d\n", num1, num2, add(num1, num2));
printf("%-5d - %5d = %5d\n", num1, num2, min(num1, num2));
printf("%-5d x %5d = %5d\n", num1, num2, multi(num1, num2));
printf("%-5d / %5d = %5d\n", num1, num2, div(num1, num2));
return 0;
}
간단한 정수 계산 프로그램입니다.
이것을 나누어봅시다.
먼저 헤더파일을 만듭니다.
Visual Studio의 경우에는
헤더파일에 새 항목을 추가하여
원하는 이름의 헤더파일을 생성합니다.
(확장자는 h입니다.)
GCC를 사용하는 vim의 경우에는 똑같은 폴더에 .h이름으로 헤더파일을 하나 만들어주시면됩니다.
(예: vim calc.h)
이제 헤더파일에 넣을 것들을 추려야합니다.
1. #define 2. enum (다음 강의에서 배웁니다.)
3. struct 선언부
4. class 선언부 (C++에 해당됩니다.)
5. 함수형
|
먼저 가장 쉽게 보이는 것은 5번인 함수형입니다.
int add(int num1, int num2)
int min(int num1, int num2)
int multi(int num1, int num2)
int div(int num1, int num2)
메인함수를 제외한 특정 기능을 하는 함수는 이렇게 4가지가 있습니다.
이 함수들을 헤더파일로 옮겨줍시다.
#pragma once
int add(int num1, int num2);
int min(int num1, int num2);
int multi(int num1, int num2);
int div(int num1, int num2);
이렇게 헤더파일에 넣어주었습니다.
이제 이 함수들을 정의해줄 c파일을 하나 더 만들어봅시다.
Visual Studio의 경우에는
소스파일에 새항목 추가를 눌러서
헤더파일의 이름과 동일한 이름으로 c파일을 만들어줍니다.
Vim의 경우에는 헤더파일 만들때와 동일하게 만들어주면 됩니다.
(예: vim calc.c)
이제 위에서 만든 헤더파일에 선언했던 함수를 재정의하여봅시다.
#include "calc.h"
int add(int num1, int num2)
{
return num1 + num2;
}
int min(int num1, int num2)
{
return num1 - num2;
}
int multi(int num1, int num2)
{
return num1 * num2;
}
int div(int num1, int num2)
{
if (num2 != 0)
{
return num1 / num2;
}
else
{
return 0;
}
}
이때 include는 <>가 아닌 ""를 사용하는거 이전시간에 배웠었죠?
(동일 프로젝트 폴더의 파일을 include 할때는 "")
이제 메인함수가 있는 C파일로 가서 수정해줍시다.
#include <stdio.h>
#include "calc.h"
int main()
{
int num1 = 15, num2 = 12;
printf("%-5d + %5d = %5d\n", num1, num2, add(num1, num2));
printf("%-5d - %5d = %5d\n", num1, num2, min(num1, num2));
printf("%-5d x %5d = %5d\n", num1, num2, multi(num1, num2));
printf("%-5d / %5d = %5d\n", num1, num2, div(num1, num2));
return 0;
}
이렇게 되면 완성된것입니다.
소스파일 분석하기 |
방금 전에 우리는 총 3개의 파일로 분할을 시켰습니다.
// calc.h
#pragma once
int add(int num1, int num2);
int min(int num1, int num2);
int multi(int num1, int num2);
int div(int num1, int num2);
헤더파일에서는 사용할 함수를 미리 선언해주고
// calc.c
#include "calc.h"
int add(int num1, int num2)
{
return num1 + num2;
}
int min(int num1, int num2)
{
return num1 - num2;
}
int multi(int num1, int num2)
{
return num1 * num2;
}
int div(int num1, int num2)
{
if (num2 != 0)
return num1 / num2;
else
return 0;
}
헤더파일과 같이 만들어준 C파일에서는 헤더파일에 선언해준 함수를 정의해줍니다.
// main.c
#include <stdio.h>
#include "calc.h"
int main()
{
int num1 = 15, num2 = 12;
printf("%-5d + %5d = %5d\n", num1, num2, add(num1, num2));
printf("%-5d - %5d = %5d\n", num1, num2, min(num1, num2));
printf("%-5d x %5d = %5d\n", num1, num2, multi(num1, num2));
printf("%-5d / %5d = %5d\n", num1, num2, div(num1, num2));
return 0;
}
흐름은 이렇습니다.
헤더파일에서 가져다 쓴다고 생각하시면 쉽습니다.
그리고 그 헤더파일에 선언된 함수들은 동일한 이름의 C파일에 정의가 되어있습니다.
#pragma once |
헤더파일의 첫줄에 우리는 전처리기 구문을 한줄 썼습니다.
#pragma once
이 구문은 "해당 헤더파일을 한 번만 포함시켜라" 라는 뜻입니다.
이것이 왜 있느냐 하면
이렇게 include가 겹칠시에 에러가 발생합니다.
같은 파일을 두 번 include 했기 때문이죠.
그래서 위의 구문을 사용하여 이미 include가 되어있다면 다시 include를 못하게 미리 방지하는 것입니다.
이 pragma once는 현재는 대부분의 컴파일러에서 사용할 수 있습니다.
(출처: 위키백과)
include guard |
위에서 봤던 pragma once를 사용하기 이전에 썼던 "include guard"라는 것이 있습니다.
#ifndef _CALC_H
#define _CALC_H
int add(int num1, int num2);
int min(int num1, int num2);
int multi(int num1, int num2);
int div(int num1, int num2);
#endif
#ifndef를 이용하여 해당 헤더파일이 선언되어있지 않을 때만 선언하여 사용하도록 합니다.
작동하는 것은 #pragma once와 같은 목적으로 작동하지만 성능은 #pragma once가 더 좋다고 합니다.
include guard의 경우에는 헤더파일을 실행하지 않더라도 전체를 읽어야 하기 때문에 #pragma once 보다 느리다고 합니다.
물론 #pragma once도 단점이 있다고 하지만 우리는 여기까지만 알아봅시다.
다음 시간에는 |
다음 시간에는 전역변수에 대해서 알아봅시다.