헤더 파일과 소스코드 파일의 분리
하나 혹은 두개의 함수 정도만 작성한다면, 코드를 분리할 필요가 없지만, 앞으로 여러분이 작성하게 될 프로그램은 그 길이가 얼마든지 길어질 수 있습니다.
보통의 경우 하나의 소스코드 파일은 하나의 오브젝트 파일이 되므로, 소스코드를 조금이라도 수정하면
소스 파일 전체를 다시 컴파일해야 하는 불상사가 벌어집니다.
또한, 하나의 소스코드에 여러 기능들이 복합적으로 포함되면, 수정하고자 하는 부분을 찾고자 할 때, 상당한 시간이 들어가게 됩니다.
헤더 파일에 함수 원형들을 공개하고, 다른 소스코드상에서 이 헤더를 포함함으로써 어떤 함수들이 존재하는지 알 수 있습니다.
이 정보를 바탕으로 컴파일을 하고, 링커는 해당하는 오브젝트 파일을 링크할 수 있습니다.
헤더 파일과 소스코드 파일의 분리방법
main.cpp 파일
#include "main.h"
int main(int argc,char** argv)
{
printf("Main Function\n");
Function();
}
main.h 파일
#ifndef __MAIN_H
#define __MAIN_H
#include <stdio.h>
void Function();
#endif
main2.cpp 파일
#include "main.h"
void Function()
{
printf("Call Function");
}
".h" 확장자를 가지는, 헤더 파일은 함수의 정의나 추후 등장할 구조체, 클래스들의 정의를 할 수 있습니다.
- 헤더 파일은 직접적으로 오브젝트 파일로 컴파일 되는것이 아님을 주의해 주세요.
main.cpp 를 살펴보면, #include 전처리구문이 등장합니다.
- main.h를 찾아오는데, ""를 사용하였기 때문에, 현재 main.cpp 가 위치한 디렉토리로 부터 파일을 찾습니다.
이 전처리문에 의해 main.cpp 파일은 main.h 파일을 그대로 포함하게 됩니다.
Header Guard
#ifndef __MAIN_H
#define __MAIN_H
#include <stdio.h>
void Function();
#endif
int main(int argc,char** argv)
{
printf("Main Functionn");
Function();
}
Header Guard 라고 하는 조금 특별한 전처리기 사용법을 설명합니다. 헤더파일에 #ifndef 로 시작하여 #endif로 끝나는 블럭, 그리고 그 안의 #define문이 존재합니다.
이는, 헤더 파일이 여러번 포함되는 것을 막기위한 조치입니다.
헤더 파일이 여러 번 포함되면 같은 이름의 함수 혹은 자료형들이 여러 벌 생기며 컴파일 오류가 발생합니다.
중복하여 include 하지 않으면 이 문제는 발생하지 않습니다만, 아래와 같은 시나리오가 존재할 수 있습니다.
- 누군가가 만들어서 제공한 A 헤더 파일에 또 다른 누군가가 만든 B 헤더 파일이 포함되어 있음.
- 이미 A 헤더 파일을 사용 중이지만, A 헤더 파일이 B 를 사용하고 있다고 인지하지 못 한 상태에서, B 헤더 파일을 추가로 참조 시도.
단순한 프로젝트라면 금방 찾아낼 수 있겠지만, 파일의 수가 많아지면 찾아내기가 상당히 어렵습니다.
만일 해당하는 정의가 없다면 #define으로 정의 해 주고, 이하에 들어있는 선언들을 포함시킵니다. 그렇지 않을경우 이미 이 헤더파일이 불러진 상태이므로 무시한다는 의미로써 작성합니다.
- 보통 __MAIN_H 처럼 파일 이름을 많이 사용합니다.
MS VC++ 이나 몇몇 컴파일러는 #pragma once 라는 전처리문이 위의 코드와 같은 일 합니다.
- pragma 전처리문의 경우 컴파일러마다 다르게 동작합니다.
컴파일러와 링커의 협력
앞서 본 헤더파일에는 Function 함수의 선언이 존재합니다.
그리고 main.cpp 에는 그 구현부가 없으며, 대신 이 구현부를 main2.cpp 파일에서 찾을 수 있습니다.
컴파일러는 소스코드를 컴파일하여 오브젝트 파일로 만드는 일을 하며,
함수를 부를 수 있도록 미리 준비만 해 놓을 뿐 실제 함수들의 주소를 연결하지는 않습니다.
링커는 이러한 오브젝트 파일들에서 함수 정보들을 찾아, 서로 연결하고 하나의 실행파일을 만들어 내는 역활을 합니다.
만일 구현부를 어떠한 오브젝트 파일에서도 찾지 못 하면 링킹 오류가 발생하게 됩니다.