본문 바로가기
~2024.10

[c언어] 다중 소스 분할 컴파일

by HJfan 2024. 2. 18.

00. 서론

c언어 학습을 위해 예제 코드를 작성하거나, 학부생 레벨의 간단한 프로젝트를 구현할 때에는

하나의 파일(예를 들어 main.c) 안에 모든 코드를 작성해도 아무 문제가 없었다.

하지만 규모가 큰 코드를 작성하게 되면서 하나의 파일에 모든 코드를 작성하는 것은 가독성과

코드 관리(유지보수나 재사용성) 측면에서 불편함을 느끼게 됐고, 소스를 분할하는 방법에 대해서 공부해 보았다.


01. 헤더 파일 추가

소스를 분할하기 위해서는 헤더 파일을 생성해야 한다.

헤더 파일 추가 방법

헤더 파일명은 자유롭게 설정해도 되지만, 확장자는 반드시 .h로 지정해 주어야 한다.

헤더 파일을 생성하면 #pragma once라는 코드가 있을 텐데 c언어를 사용하여 코드를 작성할 때는 지워도 상관없다.

이렇게 생성한 헤더 파일에는 어떤 정보를 포함해야 할까? 하기에 설명하도록 하겠다.

 

ㆍinclude guard

   소스 분할 컴파일 예제를 보면, 헤더 파일의 제일 상단과 하단에 아래와 같은 코드가 있는 것을 자주 봤을 것이다.

#ifndef _TEST_H_   
#define _TEST_H_   

...

#endif

소스를 분할 컴파일할 때, 주의해야 할 점은 헤더 파일을 중복으로 포함하는 것인데, 위 3줄의 코드를 헤더 파일에 포함하면컴파일러가 알아서 중복으로 포함된 헤더를 실행하지 않고, 한 번만 포함된 것으로 인식한다.

(만약 왜 매크로 이름에 밑줄을 접두사로 사용하는지 혹은 왜 대문자로 사용하는지에 대해 궁금한 사람들은 매크로

 이름의 관습에 대해 찾아보는 것을 추천한다.)

 

ㆍ함수 원형(함수 프로토타입)

###헤더 파일###

int sum(int a, int b); #함수 원형

위 예시 코드를 보면 알 수 있겠지만, sum이라는 사용자 정의 함수를 선언하기만 하고, 구체적인 내용을 구현하지 않았다.

헤더 파일에서 함수 구현까지 해도 에러를 발생하지는 않지만, 좋은 코드를 작성하는 습관을 위해 함수의 구현은

소스 파일에서 하도록 하자. (함수 원형에 매개 변수명을 정의하는 것도 취향 차이다. int, int만 써도 됨)

 

ㆍ전역 변수 선언

    헤더 파일에서 전역 변수를 선언할 때는 'extern' 키워드를 사용하여 다른 소스 파일에서도 해당 변수를

    사용할 수 있도록 해주어야 한다. 이를 통해 변수가 한 번만 정의되고 다른 여러 소스 파일에서 공유될 수 있는 것이다.

    또한 extern 키워드의 중요한 이점은 링킹 과정에서 중복 정의 에러를 방지한다는 것이다. 여러 소스에서 변수를

    사용하고자 할 때, 'extern' 키워드 없이 변수를 선언하면 각 파일마다 별도의 인스턴스가 생성되어 링킹 시 에러가

    발생할 수 있다.

 

ㆍ상수 정의(#define)

 

ㆍ타입 정의(typedef)

 

ㆍ매크로 정의


02. 소스 파일 추가

여기서 소스 파일은 main.c가 아닌 1번에서 생성한 헤더 파일의 정보를 사용하는 소스 파일을 의미한다.

소스 파일에는 어떤 정보가 포함될까?

ㆍ함수 정의

   헤더 파일에 선언된 함수 프로토타입의 구체적인 구현을 해준다. 1번에서 선언한 sum 함수를 예로 들면

##소스 파일##

int sum(int a, int b){
	return a+b;
}

이와 같이 구체적인 내용을 구현하는 것이다.

 

ㆍ지역 변수

 

ㆍ프라이빗 함수


03. 실제 사용 예시(전체 코드)

1번과 2번 과정을 통해 a와 b라는 정수를 매개변수로 받아 그 합을 반환하는 파일이 생성되었다.

main.c 파일에서는 이를 사용하기만 하면 된다. 전체 코드를 예시로 살펴 보자.

#헤더 파일

#ifndef _TEST_H_
#define _TEST_H_

#include <stdio.h>

int sum(int a, int b);

#endif
#소스 파일

#include "test.h"

int sum(int a, int b)
{
	return a+b;
}
#메인 소스 파일

#include "test.h"

int main()
{
	printf("%d\n", sum(10, 90));
	return 0;
}

간단한 예시 코드를 통해 C언어에서 소스를 분할 컴파일 하는 방법에 대해 다루어 보았다.

하지만 이런 간단한 예시만 봐서는 자신이 구현하고자 하는 프로젝트에 응용하는 방법이 헷갈릴 수 있다는 점에 대해

100% 공감하고 있기에, 조금 더 공부한 후 심화? 예제를 통해 다시 한 번 설명하는 글을 작성하도록 하겠다.


04. 정리 및 주의점

정리하자면, 소스 코드를 기능 별로 분할하여 컴파일하는 이유는 모듈화를 통해 코드의 재사용성을 높이기 위함이다.

이렇게 하면, 동일한 기능이 필요한 미래 프로젝트에서 코드를 새로 구현할 필요 없이, 이미 모듈화해 둔 파일을

사용하거나 일부만 수정해서 사용하기에 개발 시간을 단축하고, 유지 보수를 용이하게 하여 효율성을 높일 수 있다.

 

주의할 점은, 헤더 파일에는 함수 선언, 매크로 정의, 구조체 선언과 같은 외부에서 참조할 수 있는 선언만

포함시키고, 실제 함수의 구현이나 변수의 정의는 소스 파일(.c)에 위치시켜야 한다.

 

둘째, 전역 변수의 사용을 최소화하고, 필요한 경우 'extern' 키워드를 사용하여 헤더 파일에 선언함으로써

여러 소스 파일 간 공유가 가능하도록 해주어야 한다.

 

셋째, 헤더 파일 중복 포함을 방지하기 위해 include guard(혹은 #pragma once)를 사용해 주어야 한다.

 

물론 이 외에도 지켜야 할 규칙들이나 에러를 유발하는 상황이 더 있겠지만, 필자도 아직 배우는 단계기에

직접 실습을 해보다가 에러가 발생하는 상황이 생긴다면 지속적으로 수정하도록 하겠다.

 

※ 본 게시물은 신입 개발자가 실시간으로 공부를 하며 정리한 글이기에 다소 부정확한 표현 혹은 틀린 정보가 존재할 수도 있습니다. 잘못된 부분에 대한 피드백은 언제나 환영합니다.