본문 바로가기
Language&Framework&Etc/C

파일의 분할과 헤더파일의 디자인(27-3) 헤더파일의 디자인과 활용

by 머리올리자 2020. 12. 19.

#include 지시자의 의미를 알면 헤더파일을 완전히 이해할 수 있음

 

예시

 

헤더파일 두 개를 아래와 같은 코드로 만든다.

 

1. header1.h

{
	puts("Hello man!");

2. header2.h

	return 0;
}

main문을 아래와 같은 코드로 구성한다.

#include <stdio.h>

int main(void)
#include "header1.h"
#include "header2.h"

 

이상해보이지만 실행을 해보면 아래와 같이 결과가 나온다.

그렇다면 아래의 의미는 무엇인가

#include "header1.h"

이는 "이 문장의 위치에다가 header1.h에 저장된 내용을 가져다 놓으세요"

 

그렇다면 아래의 의미도 동일하다.

#include "header2.h"

"이 문장의 위치에다가 header2.h에 저장된 내용을 가져다 놓으세요"

 

결국 header의 내용을 불러오면서 아래의 두 코드는 같은 의미인 셈이다.

#include <stdio.h>

int main(void)
#include "header1.h"
#include "header2.h"
#include <stdio.h>

int main(void)
{
	puts("Hello man!");
	return 0;
}

 

이처럼 #include 지시자는 그 이름이 의미하듯이 파일의 내용을 단순히 포함시키는 용도로 사용된다.

 

그 이상도 이하도 아닌 단순한 '포함'

 

헤더파일을 include 하는 두 가지 방법

첫 번째

#include <헤더파일 이름>

두 번째

#include "헤더파일 이름"

 

이 둘의 유일한 차이점은 포함시킬 헤더파일의 기본 경로!

 

첫 번째 방식

  • 표준 헤더파일이 저장되어 있는 디렉토리에서 파일을 찾는다.
  • ex. stdio.h, stdlib.h, string.h 등등

두 번째 방식

  • 소스파일이 저장된 디렉토리에서 헤더파일을 찾는다.
  • 프로그래머가 정의하는 헤더파일을 포함시킬 때 사용하는 방식
  • 이 방식을 사용하면 헤더파일의 이름뿐만 아니라, 드라이브 명과 디렉토리 경로를 포함하는 '절대 경로'를 명시해서 헤더파일을 지정 가능

하지만 프로그램 개발에서는 특별한 이유가 없으면 절대경로를 사용하지 않는다.

 

절대경로를 사용하면 다른 컴퓨터에서 컴파일 하는 일이 매우 번거로워지기 때문

 

따라서 #include 문에서는 절대경로를 사용하지 않는다. 대신 상대경로를 사용한다.

 

상대경로의 지정 방법

 

절대 경로 : "절대로 경로가 변경되지 않는다. 컴퓨터를 옮겨도 지정한 경로는 변경되지 않는다."

 

상대 경로는 말 그대로 상대적인 경로

 

즉, 실행하는 컴퓨터의 환경에 따라서 경로가 바뀌기 때문에 '상대경로'

 

상대경로 예시

#include "Release\header0.h"
#include "..\c_study\header1.h"
#include "..\..\headers\header2.h"

점 두 개(..)는 상위 디렉토리를 위미한다.

 

따라서 세 번째는 다음과 같은 의미

 

"두 단계 상위 디렉토리의 하위 디렉토리인 headers에 존재하는 header2.h를 포함하라"

 

이렇듯 상대경로를 기반으로 헤더파일을 선언하면, 드라이브 명이나 디렉토리 위치에 덜 영향을 받는다.

 

헤더파일에 무엇을 담으면 좋을까?

헤더파일에는 다음과 같은 유형의 선언을 담게 된다.

extern int num;
extern int getnum(void); // extern 생략 가능

외부에 선언된 변수에 접근하거나 외부에 정의된 함수를 호출하기 위한 선언들인데, 이들은 둘 이상의 소스파일로 이뤄진 프로그램에서 당연히 삽입될 수 밖에 없는 유형의 선언들.

 

필요할 때마다 매번 삽입하는 것은 번거로움

 

따라서 이들 선언을 헤더파일에 모아두고 필요할 때마다 헤더파일으 포함시키는 방법 선택

 

구조체의 정의는 어디에? 중복은 안됨

 

구조체의 선언 및 정의는 헤더파일에 삽입하는 것이 좋다.

 

그러나 하나의 소스파일 내에서만 사용이 되는 구조체라면 소스파일에 정의하는 것도 괜찮다.

 

헤더파일의 중복삽입 문제

헤더파일을 중복해서 삽입하면 문제?

헤더파일의 중복삽입 자체는 문제가 되지 않음.

 

뜩히 선언과 같은 유형의 두 번 삽입은 컴파일 오류가 발생하지 않는다.

 

하지만 구조체의 정의는 다르다. 이는 컴파일 하는데 도움을 주는 정보가 아닌, 실행파일의 내용에 직접적인 연관이 있는 정보이다. 구조체를 어떻게 정의하느냐에 따라서 실행파일의 크기뿐만 아니라 실행파일의 내용도 달라진다. 따라서 이러한 형태의 정의는 두 번 이상 중복될 수 없다.

 

조건부 컴파일을 활용한 중복삽입 문제의 해결

중복삽입에 대한 해결책은 '조건부 컴파일을 위한 매크로'에서 찾을 수 있다.

 

stdiv2.h

#ifndef __STDIV2_H__
#define __STDIV2_H__

typedef struct div
{
	int quotient;
	int remainder;
}DIV;

#endif

 

이 파일을 처음 포함하는 소스파일은 __STDIV2_H__라는 이름의 매크로가 정의되지 않은 상태이므로 매크고 __STDIV2_H_가 정의되고 이어서 구조체 Div가 정의된다.

 

그리고 이후에 이 파일을 다시 포함하는 경우에는 매크고 __STDIV2_H__가 정의된 상태이므로 모든 내용이 포함되지 않는다.

 

즉 구조체 Diiv는 소스파일당 하나씩만 정읟횐다.

 

intdiv4.h

#ifndef __INTDIV4_H__
#define __INTDIV4_H__

#include "stdiv2.h"

Div IntDiv(int num1, int num2);

#endif

 

intdiv4.c

#include "stdiv2.h"

Div IntDiv(int num1, int num2);
{
	Div dval;
	dval.quotient = num1 / num2;
	dval.remainder = num1 % num2;
	return dval;
}

main.c

#include <stdio.h>
#include "stdiv2.h"
#include "intdiv4.h"

int main(void)
{
	Div val = IntDiv(5, 2);
	printf("몫: %d \n", val.quotient);
	printf("나머지: %d \n", val.remainder);

	return 0;
}

 

 

참고 : [윤성우 열혈 C 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.