Algorithm/C

[c언어 개념] #17 파일의 분할과 헤더파일 디자인

하노정 2022. 8. 13. 22:14

c언어 개념 정리와 문제 풀이를 통해 알고리즘 수업을 준비하는 과정입니다. 그동안 수강했던 c언어 수업 자료와 윤성우의 열혈 C 프로그래밍의 내용을 포함해 저에게 기록할 가치가 있는 내용이 담겨 있습니다. 


파일의 분할

하나의 파일로 프로그램을 작성하면, 프로그램의 크기가 커졌을 때 관리하기가 어려워진다는 문제점이 발생한다.

따라서 여러 개의 파일을 만들어서 서로 연관 있는 함수와 변수들을 구분해서 담는다.

 

컴파일러는 파일 단위로 컴파일을 진행한다. 쉽게 말해 컴파일러는 다른 파일의 정보를 참조해 컴파일을 진행하지 않는다.

외부에 선언 및 정의되었다고 컴파일러에게 알려줘야 한다. 

extern int num;

위의 선언은 변수 num을 할당하는 선언이 아니라 num의 자료형이 무엇이고, 어디에 선언되어 있는지 컴파일러에게 알려주는 메시지이다. 

extern void Increment(void);

위의 선언은 함수가 외부에 정의되어 있음을 알리는 것이다. 함수가 외부에 정의되어 있음을 알릴 때에는 extern 선언을 생략할 수 있다. 

 

컴파일러에게는 extern 선언을 통해서 함수 또는 변수가 외부에 선언 및 정의되어 있다는 사실만 알리면 된다.

 

'static 전역변수'는 변수의 접근 범위를 파일 내부로 제한하는 경우에 사용된다. 

함수에도 static 선언을 할 수 있는데, 전역변수와 마찬가지로 파일 내에서만 접근이 가능하도록 함수를 제한하는 것이다. 

이렇게 정의한 함수는 extern 선언을 하더라도 다른 파일에서는 접근이 불가능하다. 파일이 외부에서 원치 않게 호출되는 것을 막을 수 있어서 static 선언을 추가하여 코드에 안전성을 부여할 수 있다.


#include 지시자

#include 지시자는 파일의 내용을 단순히 '포함'시키는 용도로 사용된다.

#include "header.h"

'이 문장의 위치에다가 header.h에 저장된 내용을 가져다 놓으세요.'라는 의미다.

 

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

차이점은 헤더파일의 기본 경로이다.

  • #include <헤더파일 이름>

표준 헤더파일이 저장되어 있는 디렉터리에어 파일을 찾게 된다.

이 방식은 stdio.h, stdlib.h, string.h와 같은 표준 헤더파일을 포함시킬 경우 사용된다. 

  • #include "헤더파일 이름"

이 문장을 포함하는 소스파일이 저장된 디렉터리에서 헤더파일을 찾는다. 

이 방식은 프로그래머가 정의하는 헤더파일을 포함시킬 때 사용한다.

이 방식을 사용할 때는 헤더파일의 이름뿐만 아니라, 드라이브 명과 디렉터리 경로를 포함하는 '절대경로'를 명시해 헤더파일을 지정할 수 있다. 근데 절대경로를 지정해서 헤더파일을 선언하면 다른 컴퓨터에서 컴파일 하는 일이 매우 번거롭기 때문에 #include 문에서는 절대경로를 사용 안 하고 '상대경로'를 사용한다.

 

헤더파일에 담을 것

extern int num;

extern int GetNum(void);

외부에 선언된 변수에 접근하거나 외부에 정의된 함수를 호출하기 위한 선언들인데, 필요할 때마다 매번 삽입하는 것이 번거롭기 때문에 이런 선언들을 헤더파일에 모아두고 필요할 때마다 헤더파일을 포함시킨다.

 

매크로의 명령문, 일정 연산을 하는 소스파일들도 헤더파일에 넣고 필요할 때마다 헤더파일을 포함시킬 수 있다.

 

소스파일 여러 개에서 사용되는 구조체라면, 구조체의 선언 및 정의도 헤더파일에 담는 것이 좋다. 

 

근데 주의할 점이 있다. 

구조체가 두 번 정의된 형태가 되면 컴파일 에러가 발생한다.

프로그램이 복잡해질수록 소스파일과 헤더파일은 많아지고, 헤더파일의 포함관계도 복잡해진다.

때문에 헤더파일을 한 번만 포함시키도록 주의해서 프로그래밍을 하는 것은 매우 거슬리는 일이다.

따라서 보다 근본적인 대책이 필요하다. 헤더파일의 중복삽입을 고민하지 않아도 되는 근본적인 대책 말이다.

 

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

extern 선언도 두 번 이상 삽입되더라도 컴파일 오류가 나지 않는다. 단순히 컴파일러에게 전달하는 메시지이기 때문이다.

 

하지만 '구조체의 정의'는 말이 다르다.

컴파일을 하는데 도움을 주는 정보가 아니라 실행파일의 내용에 직접적인 연관이 있는 정보다. 

구조체를 어떻게 정의하느냐에 따라서 실행파일의 크기뿐만 아니라 실행파일의 내용도 달라지기 때문에

이런 형태의 정의는 두 번 이상 중복되면 안된다.

 

조건부 컴파일을 활용해 중복삽입 문제를 해결할 수 있다.

#ifndef __STDIV2_H__
#define __STDIV2_H__

typedef struct div
{
	int num;
} Div;

#endif

이 파일을 처음 포함하는 소스파일은 __STDIV2_H__라는 이름의 매크로가 정의되지 않은 상태이므로 2~8행의 내용을 포함하게 된다. 그러면 2행의 __STDIV2_H__라는 매크로가 정의되고 있어서 4~8행에 의해 구조체 Div가 정의된다.

 

그리고 이후에 이 파일을 다시 포함하게 되면 매크로 __STDIV2_H__가 정의된 상태이므로 1행과 10행 사이에 있는 모든 내용이 포함되지 않게 된다. 결국 구조체 Div는 소스파일당 하나씩만 정의된다.