본문 바로가기
코스웨어/16년 스마트컨트롤러

2016-08-31_조재찬_스터디일지_C언어-파일의 분할과 헤더파일의 디자인

by 알 수 없는 사용자 2016. 8. 31.
728x90
반응형

파일의 분할과 헤더파일 디자인


외부에 선언 및 정의되었다고 알려주는 extern


extern 선언을 생략해도 되지만,

다른 파일에 선언 및 정의되었음을 알리기위해 extern을 쓴다.



둘 이상의 파일을 컴파일하는 방법 (프로젝트에 여러 파일을 추가해 컴파일)


기존에 존재하는 파일은 Existing Item... 새로 만드려면 New Item...



전역변수의 static 선언


static int num=0; // 외부 소스파일에서 접근 불가능한 전역변수


void SimpleFunc(void)

{

....

}


함수의 static 선언


static void MinCnt(void)

{

cnt--;

}


외부 소스파일에서의 접근(호출)을 허용하지않기 위한 선언

즉 접근범위를 파일 내부로 제한한다.




헤더파일의 디자인과 활용

프로젝트에 다음 헤더파일과 소스파일을 추가해 컴파일하면 정상적인 실행이 된다. (아래 세 파일은 동일한 디렉토리에 존재해야만 함)


header1.h

1
2
{
    puts("Hello world!");


header2.h

1
2
    return 0;
}


main.c

1
2
3
4
5
#include <stdio.h>

int main()
#include "header1.h"    // 이 위치에 .h에 저장된 내용을 include
#include "header2.h"    // 이 위치에 .h에 저장된 내용을 include



헤더파일을 include 하는 첫번째 방법


#include <헤더파일 이름>

표준 헤더파일(C 표준에서 정의하는 stdio.h, stdlib.h같은 기본적으로 제공되는 헤더파일)이 저장되어 있는 디렉토리에서 파일을 찾게 됨



헤더파일을 include 하는 두번째 방법


#include "헤더파일 이름"

소스파일이 저장된 디렉토리에서 헤더파일을 찾게 됨


두번째 방법은 다음과 같이 절대 경로를 명시해서 헤더파일 지정가능

#include "C:\CPwer\MyProject\header.h"    // windows 상에서의 절대경로 지정



하지만 절대경로를 지정해서 헤더파일을 선언하면 다른 컴퓨터에서는 경로(path)가 다를 수가 있기 때문에 잘 쓰지 않는다.


따라서 다음과 같이 상대 경로를 자주 이용한다.


#include "..\..\Myheader\header2.h"




extern 선언은 대개 헤더파일에 삽입


필요한 함수를 소스파일에서 모두 extern 선언할 필요없이

헤더파일만 include하면 되므로 편하고 소스의 길이도 준다.


extern 선언은 실행파일의 크기를 증가시키거나 성능의 손해를 

보는게 아니므로 신경쓰지 않아도 된다.



헤더파일 디자인의 예




basicArith.h    

/* 매크로 PI에 대한 정의가 삽입 (매크로 명령문은 개별 파일단위로만 유효), 매크로 PI가 필요한 소스파일은 이 헤더파일은 include하면 된다.

   다른 소스파일에서 include해두면 혹여나 정의해둔 상수값이 바뀌어도 해당 헤더파일 이외의 수정은 필요없게 된다 */

1
2
3
4
5
6
#define PI 3.1415

extern double Add(double num1, double num2);
double Min(double num1, double num2);
double Mul(double num1, double num2);
double Div(double num1, double num2);


basicArith.c    // 사칙연산의 기능을 제공하는 함수들이 정의. 함수의 선언은 basicArith.h에 포함되어 있다.

double Add(double num1, double num2)
{
	return num1+num2;
}

double Min(double num1, double num2)
{
	return num1-num2;
}

double Mul(double num1, double num2)
{
	return num1*num2;
}

double Div(double num1, double num2)
{
	return num1/num2;
}


areaArith.h

1
2
double TriangleArea(double base, double height);
double CircleArea(double rad);


areaArith.c    // 면적을 구하는 함수들이 정의. 이 함수들은 basicArith.c에 정의된 Mul, Div와 같은 함수들을 호출하기 때문에 basicArith.h를 include

1
2
3
4
5
6
7
8
9
10
11
#include "basicArith.h"

double TriangleArea(double base, double height)
{
	return Div(Mul(base, height), 2);
}

double CircleArea(double rad)
{
	return Mul(Mul(rad, rad), PI);
}



roundArith.h

1
2
double RectangleRound(double base, double height);
double SquareRound(double side);


roundArith.c    // 둘레를 구하는 함수들이 정의. 이 함수들도 basicArith.c에 정의된 사칙연산과 관련된 함수들을 호출. 따라서 basicArith.h를 include

1
2
3
4
5
6
7
8
9
10
11
#include "basicArith.h"

double RectangleRound(double base, double height)
{
	return Mul(Add(base, height), 2);
}

double SquareRound(double side)
{
	return Mul(side, 4);
}


main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include "areaArith.h"
#include "roundArith.h"

int main(void)
{
	printf("삼각형 넓이(밑변 4, 높이 2): %g \n", 
		TriangleArea(4, 2));
	printf("원 넓이(반지름 3): %g \n", 
		CircleArea(3));

	printf("직사각형 둘레(밑변 2.5, 높이 5.2): %g \n", 
		RectangleRound(2.5, 5.2));
	printf("정사각형 둘레(변의 길이 3): %g \n", 
		SquareRound(3));
	return 0;
}


Output : 

삼각형 넓이(밑변 4, 높이 2): 4

원 넓이(반지름 3): 28.2735

직사각형 둘레(밑변 2.5, 높이 5.2): 15.4

정사각형 둘레(변의 길이 3): 12




구조체의 정의도 헤더파일에 넣어두고, 필요할때마다 include하는 것이 일반적


구조체의 정의도 파일단위로 선언이 유효


동일 구조체의 정의를 둘 이상의 소스파일에서 필요로 하면 

소스파일마다 정의를 추가시켜 줘야 한다.



헤더파일의 중복삽입 문제


extern int num;

void Increment(void);

바로 위와 같은 유형의 선언은 두 번 이상 삽입이 되어도

컴파일 오류가 발생하지 않는다. 이는 컴파일러에게 전달하는 메시지일뿐이기 때문이다.


하지만 구조체나 함수의 정의가 포함된 헤더파일을 두 곳 이상에서 참조하게 되면

두 번 이상 정의된 형태가 되어 컴파일 에러가 발생한다.



헤더파일의 중복삽입 문제.7z



따라서 이 문제는 조건부 컴파일을 활용해 해결한다.


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


#ifndef __STDIV2_H__    // __STDIV2_H__    매크로가 정의되어 있지 않다면

#define __STDIV2_H__    // __STDIV2_H__    매크로를 정의하라 (정의되어 있다면 포함되지 않음)



/* 이 파일을 처음 포함하는 소스파일은 __STDIV2_H__가 정의되있지 않은 상태이므로 2~8행까지 포함

   따라서 2행에 의해 매크로 __STDIV2_H__가 define, 4~8행에서 구조체 Div가 정의된다.


   이후 다른 파일에서 다시 포함하는 경우네는 매크로 __STDIV2_H__가 정의된 상태이므로 1~10행의 모든 내용이 포함되지 않는다. 

   따라서 구조체 Div가 소스파일당 하나씩 정의되게 된다.

 */

stdiv2.h    

1
2
3
4
5
6
7
8
9
10
#ifndef __STDIV2_H__
#define __STDIV2_H__

typedef struct div
{
	int quotient;   // 몫
	int remainder;  // 나머지
} Div;

#endif


intdiv4.h

1
2
3
4
5
6
7
#ifndef __INTDIV4_H__
#define __INTDIV4_H__

#include "stdiv2.h"
Div IntDiv(int num1, int num2);

#endif


intdiv4.c

1
2
3
4
5
6
7
8
9
#include "stdiv2.h"

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


main.c

1
2
3
4
5
6
7
8
9
10
11
#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; 
}


main.c에 2행과 3행에서 stdiv2.h를 두 번 포함하려고 한다.

하지만 stdiv2.h에 삽입된 매크로 지시자 #ifndef~#define~#endif에 의해 중복 삽입 문제는 생기지 않는다.


헤더파일에 존재하는 내용은 #ifndef~#define~#endif를 이용해 중복삽입 문제를 미연에 방지하는 것이 좋다.

728x90