본문 바로가기
c언어

c언어 다중 소스 파일에 대하여 알아보기

by 개발자 L 2023. 2. 22.
반응형

c언어 다중 소스 파일에 대하여 알아보기

네 안녕하세요, 이번 포스팅에서는 다중 소스 파일에 대하여 알아보도록 하겠습니다.

지금까지 제가 설명드렸던 것들은 전부 단일 파일로 이루어진 것들이었습니다.

하지만 실제로 우리가 작업을 하다 보면 정말 많은 파일들을 연결해서 하나의 파일로 쏴서 출력하는 걸 하게 됩니다.

그걸 우리는 다중 소스 파일이라고 합니다.

그래서 이번 포스팅에서는 이 다중 소스 파일에 대하여 자세히 알아보는 시간을 가져보려고 합니다.

그럼 지금부터 시작하도록 하겠습니다.

 

1. 소스 파일을 여러 개를 만드는 이유

우리가 소스 파일을 여러 개를 만드는 이유는 서로 관련이 있는 함수들은

같이 모아서 저장하면 다음에 그 함수를 쓸 일이 생기면 그냥 호출만 하면 되기 때문에

오히려 그 편이 가독성도 뛰어나지고, 코드 간 연결도 매끄러워져서 수정도 쉬워집니다.

예를 들자면 이런 겁니다.

우리가 어떤 수의 거듭 제곱을 구하는 함수를 구현해 놓았다고 가정을 해봅시다.

그러고 나서 새로운 함수를 만드는데,

그 함수 내에서 거듭제곱을 쓰는 함수를 필요로 합니다.

일반적인 경우라면 그 함수 내에 거듭제곱을 구하는 새로운 함수를 만들어서 한 페이지 내에서 구현했을 건데,

그러지 않고 전처리기를 통해서 가지고만 오면 된다는 것입니다.

그리고 이러한 소스 파일들을 '모듈(module)'이라 부르고,

이러한 다중 소스 파일들로 엮어서 소스를 짜서 하나의 큰 프로그램을 만드는 것을 '모듈화'라고 합니다.

또한, 이 모듈들은 하나의 소스 파일과 함수들의 원형이 정의되어 있는 헤더 파일을 가지고 있습니다.

그래서 실행 원리는 이렇습니다.

  • 거듭제곱을 구하는 함수의 소스 파일(power.c)
  • power.c의 객체 파일 컴파일
  • 메인 함수가 들어갈 소스 파일(main.c)
  • main.c의 객체 파일 컴파일
  • power.c와 main.c 파일 링크
  • 파일 실행

이러한 원리로 가게 되고,

제가 그러면 이걸 직접 코드를 작성해서 보여드리도록 하겠습니다.

#pragma once

// power.c에 대한 헤더 파일
double power(int x, int y); // 함수 원형 정의

이건 power.c라는 모듈과 세트인 헤더 파일입니다.

power.c에서 이 헤더 파일을 찾아야 컴파일이 가능합니다.

그럼 power.c는 어떻게 작성이 되었는지 보여드리도록 하겠습니다.

#include "power.h" // 사용자가 만든 헤더 파일은 쌍따옴표 사용

double power(int x, int y)
{
    double result = 1.0;
    int i;

    for (i = 0; i < y; i++)
    {
        result *= x;
    }

    return result;
}

이런 식으로 작성을 합니다.

그리고 여기서 눈여겨봐야 할 것은 헤더 파일 포함 방식입니다.

일반적인 내장 라이브러리 헤더 파일은 꺽쇠(<>)를 사용했는데,

개발자가 직접 만든 헤더 파일은 큰따옴표(" ")를 사용했습니다.

이렇게 해주는 이유는 유저 파일과 내장 파일을 구별해 주기 위함이며,

이걸 해주지 않으면 컴파일러가 혼동을 할 수 있어서 꼭 해주어야 합니다.

그리고 메인 파일의 경우는 이런 식으로 썼습니다.

#include <stdio.h>
#include "power.h"

int main()
{
    int x, y;

    printf("x의 값을 입력하세요 : ");
    scanf("%d", &x);

    printf("y의 값을 입력하세요 : ");
    scanf("%d", &y);

    printf("%d 의 %d 제곱 값은 %f 입니다.\n", x, y, power(x, y)); // 외부 파일에 정의된 함수 사용

    return 0;
}

이렇게 작성을 했습니다.

power.h 헤더 파일을 가져옴으로써 power.c 파일이 main.c 파일 안으로 링크가 됩니다.

또한, 해당 파일들의 디렉터리는 이렇습니다.

다중 소스 파일 디렉터리

이런 식의 구조로 짜여 있습니다.

그럼 결과도 보도록 하겠습니다.

x의 값을 입력하세요 : 2
y의 값을 입력하세요 : 3
2 의 3 제곱 값은 8.000000 입니다.

결과도 잘 나온 것을 확인할 수 있습니다.

 

2. 헤더 파일을 사용하는 이유

헤더 파일을 사용하는 이유는 단순합니다.

소스 코드의 중복을 방지하고,

가독성을 높이기 위함입니다.

기본적으로 어떤 함수를 호출하기 위해서는 이러한 조건이 붙습니다.

  • 같은 디렉터리 내에 작성이 된 소스 파일일 경우 호출이 가능하다.
  • 한 페이지 내에 작성되어 있는 함수의 경우 호출이 가능하다.

보통은 이러한 조건을 가지는데,

이걸 일일이 호출하는 것도 꽤나 힘든 일이고,

무엇보다도 소스 파일이 많다면 중복을 피하기가 매우 힘듭니다.

그래서 해당 소스 파일들에 있는 함수의 원형을 사용자 지정 헤더 파일에 정의를 해두고,

그걸 본 소스에 포함을 시켜주면 이전에 만들어둔 소스 파일들과 링크가 되면서 돌아갑니다.

그 방법이 훨씬 안전하고 효율이 좋습니다.

반응형

 

3. 다중 소스 파일과 외부 변수

우리가 이전에 배운 변수는 크게 두 가지가 있습니다.

  • 지역 변수
  • 전역 변수

여기서 지역 변수는 함수가 호출되면서 실행이 되는 변수이기 때문에 외부 변수로 쓰이질 못합니다.

하지만 지역 변수의 경우에는 외부 변수로 호출이 가능합니다.

우리가 예전에 여러 가지 지정자들에 대하여 배웠는데,

그중에 'extern'이라는 지정자를 이야기했을 겁니다.

이 지정자는 외부 연결 지정자인데,

이러한 변수들을 참조 가능하게 만들어주는 기능을 합니다.

예를 들어 이러한 지역 변수가 있다고 해봅시다.

double gx, gy;

이렇게만 써두면 이 변수가 들어있는 파일 내에서만 사용이 가능한데,

여기에 이거 하나만 추가하면 상황은 달라집니다.

extern gx, gy;

이렇게 선언을 해주면 이 전역 변수가 선언이 된 본 소스 파일에서는 물론,

해당 디렉터리 내에 있는 모든 소스 파일들은 이 변수를 공유할 수 있게 됩니다.

전역 변수와 지역 변수, 그리고 extern 지정자에 대하여 더 알고 싶으신 분들은

제가 기재해 둔 글이 있으니, 그 부분을 참고하시면 도움이 될 것 같습니다.

 

2022.12.06 - [분류 전체 보기] - c언어 지역 변수 알아보기

 

c언어 지역 변수 알아보기

c언어 지역 변수 알아보기 네 안녕하세요, 이번 포스팅에서는 c언어에서 사용하는 변수 중 하나인 지역 변수에 대하여 알아보겠습니다. c언어에서 사용하는 변수는 크게 두 가지로 나뉩니다. 바

funnycoderl.tistory.com

 

2022.12.06 - [분류 전체보기] - c언어 전역 변수 알아보기

 

c언어 전역 변수 알아보기

c언어 전역 변수 알아보기 네 안녕하세요, 이번 포스팅에서는 c언어에서 사용하는 변수 중 하나인 전역 변수에 대하여 알아보도록 하겠습니다. 어쩌면 전역변수의 기능은 지역변수보다 중요할

funnycoderl.tistory.com

 

2022.12.06 - [분류 전체보기] - c언어 연결 알아보기(외부 연결, 내부 연결, extern 지정자, static 지정자)

 

c언어 연결 알아보기(외부 연결, 내부 연결, extern 지정자, static 지정자)

c언어 연결 알아보기(외부 연결, 내부 연결, extern 지정자, static 지정자) 네 안녕하세요, 이번 포스팅에서는 연결에 대하여 알아보도록 하겠습니다. 우리가 코드를 작성을 하고 나서 여기저기에

funnycoderl.tistory.com

 

이렇게 있는데, 보시면 도움이 될 겁니다.

 

4. 구조체, typedef 매크로 정의하기

구조체와 typedef, 매크로 등을 여러 소스 파일에 걸쳐 공유해 사용하기 위해서는

이들을 헤더 파일에 넣어주는 것이 좋습니다.

그 이유는 보통 구조체나 typedef는 사용자가 직접 정의를 내리기 때문에

오히려 그렇게 헤더 파일 내에 원형을 선언해 주고,

그걸 불러다 쓰는 것이 소스 파일 수정에도 용이하고,

가독성도 높아집니다.

그럼 제가 그 예시를 직접 소스를 작성해서 보여드리도록 하겠습니다.

이번에 보여드릴 것은 다중 소스 파일을 이용해서 직사각형을 표현해보려 합니다.

먼저 헤더 파일을 보여드리도록 하겠습니다.

#pragma once

struct rect
{
	int x, y, w, h;
};

typedef struct rect RECT;

void draw_rect(const RECT*); 
double calc_area(const RECT*);
void move_rect(RECT*, int, int);

헤더 파일에는 구조체와 typedef, 그리고 함수의 원형들이 정의가 되어있습니다.

그럼 이제 이 헤더 파일을 기반으로 작성한 rectangle.c 파일을 보여드리도록 하겠습니다.

#include <stdio.h>
#include "rectangle.h"

#define DEBUG

void draw_rect(const RECT* r)
{
#ifdef DEBUG
	printf("draw_area(x = %d, y = %d, w = %d, h = %d\n", r->x, r->y, r->w, r->h);
#endif
}

double calc_area(const RECT* r)
{
	double area;
	area = r->w * r->h;

#ifdef DEBUG
	printf("calc_area() = %f\n", area);
#endif

	return area;
}

void move_rect(RECT* r, int dx, int dy)
{
#ifdef DEBUG
	printf("move_rect(%d, %d)\n", dx, dy);
#endif

	r->x += dx;
	r->y += dy;
}

이런 식으로 써서 각각의 함수에서 값이 출력이 되게끔 만들었습니다.

그럼 이제 메인 함수의 소스를 보여드리도록 하겠습니다.

#include <stdio.h>
#include "rectangle.h"

int main()
{
	RECT r = { 10 ,10, 20, 20 };
	double area = 0.0;

	draw_rect(&r);
	move_rect(&r, 10, 20);
	draw_rect(&r);

	area = calc_area(&r);

	draw_rect(&r);

	return 0;
}

매우 간단하게 작성이 되었습니다.

그럼 이제 결과도 보도록 하겠습니다.

draw_area(x = 10, y = 10, w = 20, h = 20
move_rect(10, 20)
draw_area(x = 20, y = 30, w = 20, h = 20
calc_area() = 400.000000
draw_area(x = 20, y = 30, w = 20, h = 20

결과도 문제없이 잘 나온 것을 볼 수 있습니다.

 

여기까지 다중 소스 파일에 대하여 알아보았는데요,

다음 포스팅에서는 비트 필드 구조체에 대하여 알아보도록 하겠습니다.

긴 글 읽어주신 독자분들께 진심으로 감사드립니다~

반응형

댓글