69. 소스 파일
1. 키워드
- 소스 파일
2. 여러 소스 파일 사용하기
- 지금까지 소스 파일(
.c
) 하나로만 작업을 했다. - 하지만 프로그램이 커지다 보면 각종 구조체와 함수가 많아지게 된다.
- 따라서 공통으로 사용하는 부분은 헤더 파일에 넣고, 각종 함수들은 기능별로 소스 파일을 분리하는 것이 좋다.
- 이번에는 함수의 기능별로 파일을 분리하고, 함수 선언과 구조체는 헤더 파일에 넣는 방법을 알아보자.
3. 프로그램 설계하기
- 소스 코드를 작성하기 전에 요구사항을 분석하여 프로그램의 기능을 확정하고, 각 기능 간의 관계를 정의해야 한다.
- 예제로 만들어볼 프로그램은 덧셈, 뺄셈 프로그램이며 요구사항은 다음과 같다.
1] 덧셈
2] 뺄셈
3] 결과 출력(전용 함수 사용)
- 요구사항이 나왔으니 구현할 기능을 정해야 한다.
- 만들어야 할 함수와 구조체는 다음과 같다.
1] 계산 데이터 구조체
2] 덧셈 함수
3] 뺄셈 함수
4] 결과 출력 함수
- 덧셈과 뺄셈 함수는 계산 데이터 구조체를 받아서 계산한다.
- 그리고 결과 출력 함수는 계산 데이터 구조체의 내용을 출력한다.
- 즉, 모든 함수는 계산 데이터 구조체를 공통으로 사용한다.
- 이제 구조체와 함수의 이름을 짓고 어떤 파일에 들어가야 할지 결정해야 한다.
- 여기서는 크게 계산과 출력으로 구분해 보자.
- 프로젝트 이름은
examplecalc
이다.
4. 계산 데이터 헤더 파일 작성하기
- 먼저 계산 데이터 구조체가 들어갈 헤더 파일부터 만든다.
- 다음 내용을 프로젝트 디렉터리에
calcdata.h
로 저장한다.
#ifndef CALC_DATA_H // CALC_DATA_H가 정의되어 있지 않다면
#define CALC_DATA_H // CALC_DATA_H 매크로 정의
typedef struct _CALC_DATA // 계산 데이터 구조체 정의
{
int operand1; // 계산할 값1
int operand2; // 계산할 값2
char operation; // 연산자 문자
int result; // 계산 결과
} CALC_DATA;
#endif // #ifndef CALC_DATA_H 끝
- 계산 데이터 구조체
_CALC_DATA
(구조체 별칭CALC_DATA
)에는 계산할 값 두 개와 연산자 문자 그리고 계산 결과가 멤버로 들어간다. - 그런데 헤더 파일의 맨 위와 맨 아래에 조건부 컴파일 지시자가 들어있다.
- C의 헤더 파일은 여러 소스 파일에서 포함할 수 있다.
- 만약
calc.c
와print.c
에서calcdata.h
파일을 포함했다면calc.c
와print.c
두 파일 모두calcdata.h
파일의 내용이 들어가므로CALC_DATA
구조체의 정의 부분도 두 개가 생긴다. - 하지만 같은 이름으로 된 구조체를 두 번 정의하면 컴파일 에러가 발생한다.
- 이런 형태의 조건부 컴파일은 컴파일 에러를 방지하기 위해 사용한다.
- 즉, 다음과 같이
CALC_DATA_H
매크로가 정의되어 있지 않다면CALC_DATA_H
매크로를 정의하고, 코드를 포함한다.
#ifndef CALC_DATA_H // CALC_DATA_H가 정의되어 있지 않다면
#define CALC_DATA_H // CALC_DATA_H 매크로 정의
// 코드를 포함(구조체 정의 부분)
#endif
- 만약 다른 소스 파일에서
calcdata.h
헤더 파일을 포함하게 된다면CALC_DATA_H
매크로가 이미 정의되어 있으므로#ifndef
와#endif
사이의 코드는 포함하지 않게 된다.
- 이렇게 조건부 컴파일을 활용하면 구조체 정의 부분은 언제나 한 번만 포함되므로 컴파일 에러는 발생하지 않는다.
5. 덧셈, 뺄셈 함수 작성하기
- 이번에는
calc.h
헤더 파일에 덧셈 함수와 뺄셈 함수를 선언해 보자.
#include "calcdata.h" // CALC_DATA 구조체를 사용하기 위해 calcdata.h 헤더 파일 포함
void add(CALC_DATA *data); // 덧셈 함수 선언
void sub(CALC_DATA *data); // 뺄셈 함수 선언
- 헤더 파일은 보통 함수가 있다는 사실을 알려주는 역할만 하고 함수를 구현하지 않는다.
- 따라서
add
,sub
함수 선언만 넣는다. - 또한,
add
,sub
함수에서CALC_DATA
를 매개변수를 사용하므로#include
로calcdata.h
헤더 파일을 포함한다.
- 이제
calc.c
소스 파일에add
,sub
함수를 구현(정의)해 보자.
#include "calc.h" // CALC_DATA 구조체를 사용하기 위해 calc.h 헤더 파일 포함
// calc.h가 calcdata.h를 포함하고 있음
void add(CALC_DATA *data) // 덧셈 함수 정의
{
data->operation = '+'; // 연산자 문자 + 저장
// 계산할 값 두 개를 더해서 결과 저장
data->result = data->operand1 + data->operand2;
}
void sub(CALC_DATA *data) // 뺄셈 함수 정의
{
data->operation = '-'; // 연산자 문자 - 저장
// 계산할 값 두 개의 차를 구해서 결과 저장
data->result = data->operand1 - data->operand2;
}
- 덧셈 함수와 뺄셈 함수를 구현한다.
- 여기서는
CALC_DATA
구조체의operation
멤버에 연산자 문자를 저장하고,result
멤버에 계산 결과를 저장한다. - 또한, 함수의 매개변수로
CALC_DATA
구조체를 사용하고 있으므로 헤더 파일을 포함해야 하는데 여기서는calc.h
를 포함했다. - 왜냐하면
calc.h
가calcdata.h
를 포함하고 있으므로calc.h
만 포함해도 된다.
6. 계산 결과 함수 작성하기
- 이번에는
print.h
헤더 파일에 계산 결과 출력 함수를 선언해 보자.
#include "calcdata.h" // CALC_DATA 구조체를 사용하기 위해 calcdata.h 헤더 파일 포함
void print(CALC_DATA *data); // 결과 출력 함수 선언
- 마찬가지로
print
함수를 선언만 해준다. - 또한,
print
함수도CALC_DATA
를 매개변수로 사용하므로calcdata.h
헤더 파일을 포함한다.
- 이제
print.c
파일에print
함수를 구현한다.
#include <stdio.h> // printf 함수가 선언된 헤더 파일
#include "print.h" // CALC_DATA 구조체를 사용하기 위해 print.h 헤더 파일 포함
// print.h가 calcdata.h를 포함하고 있음
void print(CALC_DATA *data) // 결과 출력 함수 정의
{
printf("%d %c %d = %d\n",
data->operand1, // 계산할 값1
data->operation, // 연산자 문자
data->operand2, // 계산할 값2
data->result); // 계산 결과
}
- 매개변수로 받은
CALC_DATA
구조체의 내용을printf
함수로 출력해 준다. - 마찬가지로
CALC_DATA
구조체를 사용해야 하므로print.h
파일을 포함한다.
7. main
함수 작성하기
- 마지막으로
main
함수가 들어갈main.c
파일이다.
#include "calc.h" // add, sub 함수가 선언된 헤더 파일. calcdata.h 헤더 파일을 포함하고 있음
#include "print.h" // print 함수가 선언된 헤더 파일. calcdata.h 헤더 파일을 포함하고 있음
int main()
{
CALC_DATA data1;
data1.operand1 = 10;
data1.operand2 = 20;
add(&data1); // 덧셈 실행
print(&data1);
CALC_DATA data2;
data2.operand1 = 40;
data2.operand2 = 15;
sub(&data2); // 뺄셈 실행
print(&data2);
return 0;
}
// 10 + 20 = 30
// 40 - 15 = 25
main.c
파일에서는 지금까지 만든 구조체와 함수를 사용하면 된다.add
,sub
함수를 사용하기 위해calc.h
헤더 파일을 포함했고,print
함수를 사용하기 위해print.h
헤더 파일을 포함했다.- 여기서
CALC_DATA
구조체를 사용하기 위해calcdata.h
헤더 파일을 포함해야 하는데calc.h
,print.h
에서 이미 포함하고 있으므로calcdata.h
헤더 파일은 따로 포함하지 않는다.
- 최종 헤더 파일 포함 관계는 다음과 같다.
GCC에서 여러 소스 파일 컴파일하기
- GCC에서 여러 소스 파일을 컴파일하려면
gcc
뒤에.c
파일을 나열하고-o
옵션에 실행 파일 이름을 지정하면 된다.