[컴파일 강좌] 1강 - C 분할 컴파일

|
컴파일 강좌
1강 - C 분할 컴파일
2강 - C++ 분할 컴파일
3강 - 정적 라이브러리
4강 - 동적 라이브러리

하나의 파일에 모든 소스 코드를 넣으면 그 파일의 크기는 얼마나 될까? 그리고 여러 사람이 동시에 작업하는 것이 가능할까?
몇 백명이 투입되는 대형 프로젝트의 경우 모든 작업이 분업화되어 작은 부분을 하나하나 합쳐서 커다란 하나의 프로그램을 만든다.

그러면 각각의 파일을 컴파일하여 오브젝트 파일을 생성하고 이 오브젝트 파일을 하나로 합치는 방법에 대해서 알아보자.

예제1) main.c 파일에 모든 함수를 작성한 경우

main1.c
#include <stdio.h>

void swap(int* a, int* b)
{
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}

void disp(int a, int b)
{
    printf("a = %d, b = %d\n", a, b);
}

int main()
{
    int a = 10;
    int b = 30;

    disp(a, b);

    swap(&a, &b);

    disp(a, b);

    return 0;
}
$ gcc -o main1 main1.c


예제2) 3개의 파일로 분할한 경우

swap.c
void swap(int* a, int* b)
{
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}

disp.c
void disp(int a, int b)
{
    printf("a = %d, b = %d\n", a, b);
}

main2.c
#include <stdio.h>
#include "swap.c"
#include "disp.c"

int main()
{
    int a = 10;
    int b = 30;

    disp(a, b);

    swap(&a, &b);

    disp(a, b);

    return 0;
}
$ gcc -o main2 main2.c

main2.c는 #include문에 의해 swap.c의 소스와 disp.c의 소스가 그대로 옮겨지기 때문에 전처리가 완료된 후의 소스코드는 main1.c와 같아진다.
소스파일만 분할되어 있고 main2.c만 컴파일한 것이므로 분할 컴파일이라 할 수 없다.


예제3) 3개의 파일을 각각 컴파일하여 오브젝트 파일을 생성한 후, 하나의 실행파일을 만드는 경우

swap.c
void swap(int* a, int* b)
{
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}
$ gcc -c swap.c

disp.c
#include <stdio.h>

void disp(int a, int b)
{
    printf("a = %d, b = %d\n", a, b);
}
$ gcc -c disp.c

main3.c
void swap(int*, int*);
void disp(int, int);

int main()
{
    int a = 10;
    int b = 30;

    disp(a, b);

    swap(&a, &b);

    disp(a, b);

    return 0;
}
$ gcc -c main3 main3.c

main() 함수에 있던 #include문이 다 없어졌다.
일단 아래의 두 줄은 각각의 파일을 따로 컴파일하였으므로 당연히 필요가 없다.
대신 main() 함수 안에서 쓰이는 두 함수의 원형을 선언해 주었다.

다음으로 #include <stdio.h>에 대해 살펴 보자.
main1.c나 main2.c에서는 disp() 함수에서 printf() 함수가 쓰이고 있다.
그래서 #include <stdio.h>를 써주었으나 이번에는 각각의 파일을 따로 컴파일하는 것이므로 printf() 함수가 쓰이고 있는 disp.c에 #include <stdio.h>를 써주어야 한다.
그리고 main3.c에서는 #include <stdio.h>를 지웠다.

각각의 파일을 컴파일하면 3개의 오브젝트 파일이 생성된다.
그러면 최종적으로 3개의 파일을 합쳐 보자.
$ gcc -o main3 swap.o disp.o main3.o


하나의 프로그램을 만드는데 100명이 투입되었다고 해 보자.
1명당 함수를 10개씩 만들어야 한다.
그러면 main.c 파일에 선언되어야 하는 함수의 원형은 몇 개일까? 1000개이다.
main.c를 만드는 사람은 1000개의 함수 원형을 선언해야 한다.
그런데 #include문을 생각해 보자.
#include문은 해당 파일의 내용을 그대로 붙여 넣어 준다.
그러면 각각의 개발자가 자신이 만든 함수의 원형 10개만 따로 파일로 작성해서 main.c를 만드는 사람에게 넘겨주면 되는 것 아니겠는가?
main.c를 만드는 사람은 100개의 파일을 받아서 그 파일들만 #include문으로 추가해 주면 된다.
그러면 나머지 900라인을 쓸 필요가 없어지는 것이다. 이 파일이 바로 .h로 끝나는 헤더파일이다.
물론 헤더 파일이 필요한 이유가 이것 때문만은 아니다.

예제4) 헤더 파일을 활용하여 각각의 파일을 컴파일하는 경우

swap.c와 disp.c는 예제3과 동일하다.

swap.h
void swap(int* a, int* b);

disp.h
void disp(int a, int b);

main4.c
#include "swap.h"
#include "disp.h"

int main()
{
    int a = 10;
    int b = 30;

    disp(a, b);

    swap(&a, &b);

    disp(a, b);

    return 0;
}

swap.h와 disp.h에서 각각의 함수의 원형을 선언해 주고, main4.c에서 선언되어 있던 함수의 원형을 지우고 두 개의 헤더파일을 포함시켰다.
결국 전처리가 끝나면 main3.c와 같아진다.
하지만 각각의 헤더 파일에 함수가 10개씩 들어 있다면 main3.c의 경우는 20줄을 적어야 했으나 main4.c는 2줄만 적어주면 된다.

'Compile 강좌' 카테고리의 다른 글

라이브러리  (0) 2010.06.26
[컴파일 강좌] 2강 - C++ 분할 컴파일  (0) 2010.06.22
컴파일러 옵션  (0) 2010.05.30
And