Skip to content

69. 소스 파일


1. 키워드

  • 소스 파일


2. 여러 소스 파일 사용하기

  • 지금까지 소스 파일(.c) 하나로만 작업을 했다.
  • 하지만 프로그램이 커지다 보면 각종 구조체와 함수가 많아지게 된다.
  • 따라서 공통으로 사용하는 부분은 헤더 파일에 넣고, 각종 함수들은 기능별로 소스 파일을 분리하는 것이 좋다.


001


  • 이번에는 함수의 기능별로 파일을 분리하고, 함수 선언과 구조체는 헤더 파일에 넣는 방법을 알아보자.


3. 프로그램 설계하기

  • 소스 코드를 작성하기 전에 요구사항을 분석하여 프로그램의 기능을 확정하고, 각 기능 간의 관계를 정의해야 한다.
  • 예제로 만들어볼 프로그램은 덧셈, 뺄셈 프로그램이며 요구사항은 다음과 같다.


1] 덧셈

2] 뺄셈

3] 결과 출력(전용 함수 사용)


  • 요구사항이 나왔으니 구현할 기능을 정해야 한다.
  • 만들어야 할 함수와 구조체는 다음과 같다.


1] 계산 데이터 구조체

2] 덧셈 함수

3] 뺄셈 함수

4] 결과 출력 함수


  • 덧셈과 뺄셈 함수는 계산 데이터 구조체를 받아서 계산한다.
  • 그리고 결과 출력 함수는 계산 데이터 구조체의 내용을 출력한다.
  • 즉, 모든 함수는 계산 데이터 구조체를 공통으로 사용한다.


002


  • 이제 구조체와 함수의 이름을 짓고 어떤 파일에 들어가야 할지 결정해야 한다.
  • 여기서는 크게 계산과 출력으로 구분해 보자.


003


  • 프로젝트 이름은 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.cprint.c에서 calcdata.h 파일을 포함했다면 calc.cprint.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 사이의 코드는 포함하지 않게 된다.


#ifndef CALC_DATA_H // CALC_DATA_H가 정의되어 있으면

// 코드를 포함하지 않음

#endif CALC_DATA_H


  • 이렇게 조건부 컴파일을 활용하면 구조체 정의 부분은 언제나 한 번만 포함되므로 컴파일 에러는 발생하지 않는다.


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를 매개변수를 사용하므로 #includecalcdata.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.hcalcdata.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 헤더 파일은 따로 포함하지 않는다.


  • 최종 헤더 파일 포함 관계는 다음과 같다.


004


GCC에서 여러 소스 파일 컴파일하기

  • GCC에서 여러 소스 파일을 컴파일하려면 gcc 뒤에 .c 파일을 나열하고 -o 옵션에 실행 파일 이름을 지정하면 된다.


gcc calc.c print.c main.c -o examplecalc
./examplecalc

References