[C] 분할 컴파일 / 헤더 중복문제 해결

방법이있지·2025년 6월 26일

[정글 4-8주차] C언어

목록 보기
23/26

여러 소스 파일을 분할 컴파일하여 하나의 실행 파일을 만드는 과정에 대해 알아봅시다.

분할 컴파일

  • 여러 소스 코드를 독립적으로 작성해, 하나의 프로그램으로 만들 수 있음
  • (1) 각 소스 코드를 별도의 목적 파일로 컴파일
    • 윈도우 기준 .obj / 리눅스 기준 .o
  • (2) 컴파일된 목적 파일을 링크해, 하나의 실행 파일을 만듦
    • 윈도우 기준 .exe / 리눅스 기준 확장자 없음

예제

  • 아래 main.c, input_num.c, times.c는 동일 폴더에 위치함
// main.c
#include <stdio.h>

void input_num(int *, int *);       // 두 정수를 입력
int times(int, int);                // 첫 정수를 두번째 정수로 곱함

int main(void){
    int a, b, result;
    input_num(&a, &b);
    result = times(a, b);           // 계산 결과를 result에 저장
    printf("%d와 %d의 곱: %d\n", a, b, result);

    return 0;
}
  • main 함수를 정의. input_num, times 함수는 선언만 하고 정의하지 않음.
// input_num.c
#include <stdio.h>

void input_num(int *pa, int *pb){
    printf("곱할 두 정수를 입력하시오: ");
    scanf("%d%d", pa, pb);
}
  • input_num 함수를 정의.
// times.c
#include <stdio.h>

int times(int a, int b){
    return a * b;
}
  • times 함수를 정의.

분할 컴파일 방법

  • (1단계) 각 소스 파일을 목적 파일로 컴파일
# gcc -c 소스파일명 -o 목적파일명
gcc -c main.c -o main.obj
gcc -c times.c -o times.obj
gcc -c input_num.c -o input_num.obj
  • (2단계) 각 목적 파일들을 하나로 링크해 실행 파일을 만듦
# gcc (목적파일명 여러개) -o 실행파일명
gcc main.obj times.obj input_num.obj -o times_program.exe
  • 아래 코드로 한꺼번에 수행하는 것도 가능
# gcc (소스파일명 여러개) -o 실행파일명
gcc main.c times.c input_num.c -o times_program.exe
  • (3단계) 파일을 실행
./times_program
곱할 두 정수를 입력하시오: 13 20
1320의 곱: 260

externstatic의 용도

  • extern: 다른 파일에 선언된 전역 변수를 사용한다.
    • extern 사용 없이 다른 파일의 전역 변수와 같은 이름으로 변수를 선언하면, 이름이 중복되는 다른 변수로 인식되어 오류가 발생할 수 있음
    • extern 선언과 동시에 값을 초기화할 수 없음 (e.g., extern a = 5; -> 불가능)
  • static: 다른 파일에서 전역 변수를 공유하지 못하게 한다.
    • 즉, 사용 범위가 현재 파일로 제한된다.
    • 다른 파일에서 해당 변수를 extern으로 사용할 수 없음

예제

  • 아래 main.c, input_data.c, average.c는 동일 폴더에 위치함
// main.c

#include <stdio.h>

// 함수 선언
int input_data(void);       // 양수 입력 -> 합 반환
double average(void);       // 평균을 구함
void print_data(double);    // 출력

int count = 0;              // 입력 양수의 수
static int total = 0;       // 입력 양수의 합

int main(void){
    double avg;             // 입력 양수의 평균
    total = input_data();
    avg = average();
    print_data(avg);
    return 0;
}

void print_data(double avg){
    printf("입력 양수의 수: %d\n", count);
    printf("전체 합과 평균: %d, %.1lf\n", total, avg);
}
  • 전역 변수 count 선언: 다른 파일에서 extern 선언을 사용해 사용할 수 있음.
  • 정적 전역 변수 total 선언: 다른 파일에서 사용할 수 없음.
    • 단 이 경우, 다른 파일에서 total이라는 이름의 다른 변수를 만들 수 있음.
// input_data.c
#include <stdio.h>

extern int count;       // main.c 파일의 전역변수 count 공유
int total = 0;

int input_data(void){
    int num;

    while (1){
        printf("양수 입력: ");
        scanf("%d", &num);
        if (num < 0) break;
        count++;
        total += num;
    }

    return total;
}
  • extern 선언을 사용해 main.c 파일의 전역 변수 count를 사용.
  • 선언된 전역 변수 total은, main.c에서 선언된 정적 전역 변수 total과 별도의 저장 공간을 가짐.
// average.c
extern int count;   // main.c의 전역 변수 count 공유
extern int total;   // input.c의 전역 변수 total 공유

double average(void){
    return total / (double)count;
}
  • extern 선언을 사용해 main.c 파일의 전역 변수 count를 사용.
  • extern 선언을 이용해 input_num.c 파일의 전역 변수 total을 사용.
    • main.c의 정적 전역 변수 total이 아닌, input_num.c의 일반 전역 변수 total임에 유의하기.

함수의 공유

  • 함수는 기본적으로 extern 선언 없이도 다른 파일과 공유 가능.
    • extern 없이 함수 선언만으로, 다른 파일에서 정의된 함수를 사용할 수 있음
    • 물론 extern int input_data(void){...}와 같이, 명시적으로 extern을 함수 앞에 붙일 수도 있음.
  • static 키워드로 함수를 정의하면, 함수는 정의된 파일 내부에서만 사용 가능
    • 다른 파일에서는 호출될 수 없음
    • e.g., static int input_data(void){....}와 같이 정의할 수 있음

헤더 파일의 중복 문제 해결

  • 헤더 파일을 사용할 땐, 각 파일에 중복되는 코드가 생길 수 있음
    • 하나의 함수를 여러 파일에서 사용하는 경우, 같은 함수를 각 파일에서 선언해야 함
    • 한 전역 변수를 여러 파일에서 공유하는 경우, extern 변수명을 각 파일에서 선언해야 함
    • 구조체 역시 여러 파일에서 필요할 수 있음
  • 이 경우, 함수 / extern / 구조체 선언을 헤더 파일로 만들어, 각 파일에 인클루드하면 쉽게 공유 가능
  • 단, 함수나 extern 선언은 여러 번 중복돼도 상관없지만, 구조체가 중복 선언될 시 오류 발생
    • 이를 해결할 방법이 필요

예제

  • 두 개의 헤더 파일과, 하나의 소스 파일
// point.h
#ifndef _POINT_H_   // _POINT_H_ 매크로 상수가 정의되지 않은 경우
#define _POINT_H_   // _POINT_H_ 매크로 상수 정의

// 점을 나타내는 구조체 (x, y좌표)
typedef struct{
    int x;
    int y;
} Point;

#endif              // #ifndef문의 끝
  • _POINT_H_ 매크로 상수가 정의되어 있지 않을 때만, _POINT_H_ 매크로 상수를 정의하고, Point 구조체를 정의
  • 매크로 상수가 왜 필요하냐고요? 그건 뒤에서 설명합니당
// line.h
#include "point.h"

// 두 점을 연결하는 선을 정의하는 구조체
typedef struct{
    Point first;
    Point second;
} Line;
  • Line 구조체는 "point.h" 파일의 Point 구조체를 멤버로 가지므로, "point.h"를 인클루드
// main.c
#include <stdio.h>
#include "point.h"
#include "line.h"

int main(void){
    // 점 (1, 3), (5, 11)을 연결하는 선
    Line l = {{1, 3}, {5, 11}};
    Point p;    // 선의 중간점을 나타내는 구조체

    p.x = (l.first.x + l.second.x) / 2;
    p.y = (l.first.y + l.second.y) / 2;
    printf("중간점: (%d, %d)\n", p.x, p.y);

    return 0;
}
  • "point.h""line.h"를 모두 인클루드함
    • 이때 "line.h"에서도 "point.h"를 인클루드하므로, Point 구조체가 두 번 선언되는 문제가 발생할 수 있음
// point.h
#ifndef _POINT_H_   // _POINT_H_ 매크로 상수가 정의되지 않은 경우
#define _POINT_H_   // _POINT_H_ 매크로 상수 정의

// 점을 나타내는 구조체 (x, y좌표)
typedef struct{
    int x;
    int y;
} Point;

#endif              // #ifndef문의 끝
  • 이를 방지하기 위해, 헤더 파일의 처음에 매크로 상수 _POINT_H_를 정의
  • 같은 헤더 파일이 2번 이상 인클루드된 경우, 이미 _POINT_H_가 정의되었으므로 #ifndef _POINT_H_는 거짓
    • 따라서 구조체 Point가 정의되지 않음
  • 결론: 모든 헤더 파일을 만들 땐, 매크로 상수를 처음에 정의해 같은 헤더 파일이 두 번 이상 포함되지 않게 해야 한다.

연습문제

다음 중 externstatic을 사용한 전역 변수에 대한 설명 중 옳지 않은 것을 고르세요.

  1. static을 쓰면 둘 이상의 파일에서 같은 이름의 전역 변수를 각각 선언할 수 있다.
  • 옳은 설명. static으로 선언한 정적 전역 변수는 다른 파일에서 사용할 수 없으므로, 다른 파일에서 동일 이름으로 지어도 상관없다.
  1. static을 사용한 전역 변수를 다른 파일에서 extern 선언으로 공유할 수 없다.
  • 옳은 설명.
  1. extern 선언은 중복이 가능하므로 헤더 파일에 넣어 사용하는 것이 좋다.
  • 옳은 설명. extern 선언, 함수 선언은 중복 선언이 가능함. 단 구조체는 중복 선언이 안 되므로, 매크로 상수 선언 등을 통해 중복 선언을 방지해야 함.
  1. 다른 파일에 있는 static 전역 변수를 공유할 때는 static 선언이 필요하다.
  • 잘못된 설명. static으로 선언한 전역 변수는 공유할 수 없음.
profile
뭔가 만드는 걸 좋아하는 개발자 지망생입니다. 프로야구단 LG 트윈스를 응원하고 있습니다.

3개의 댓글

comment-user-thumbnail
2025년 6월 26일

공부 왜함? velog 보면 되는데

2개의 답글