여러개 소스파일 컴파일

하나의 소스파일에 모든 코드를

넣는 것은 상식적으로 너무

복잡하기 때문에 여러개의 소스파일로

나누고 이를 합쳐서 컴파일 해야 합니다.

 

여기서는 헤더파일을 포함하여

여러개의 파일을 컴파일 하는 방법을

알아보겠습니다.

 

첫번째 파일인 main.c 입니다.

stdio.h 헤더를 넣지 않고

hello.h 헤더 파일을 포함하는데

여기는 함수 hello의 프로토타입이 들어갑니다.

 

헤더파일을 감싸는 따옴표 "" 는

현재 디렉토리(소스 코드가 있는)에서

헤더 파일을 우선적으로 검색합니다.

 

<stdio.h> 이것은 컴파일러의 시스템

헤더 파일 디렉토리를 검색합니다.

(보통 컴파일러가 설치된 폴더에 위치함)

#include "hello.h"

int main()
{
    hello("GCC tutorial!");
    return 0;
}

다음은 hello.h 헤더파일입니다.

함수의 프로토타입이 나옵니다.

구현부는 아직 없습니다.

void hello(const char * name);

마지막으로 구현부 hello_fn.c 파일입니다.

여기에 stdio.h 도 있고 마찬가지로

hello.h 헤더파일을 인클루드 합니다.

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

void hello(const char * name)
{
    printf("Hello, %s!\n", name);
}

예제를 위한 것으로 내용적으로는

별내용이 없습니다. main.c 에서

hello 라는 함수를 호출하여

문자열을 출력합니다.

 

 

내용보다 주목해야 할 것은

이 파일들의 관계입니다.

 

main.c에서 호출한 함수의 프로토타입은

hello.h 에 있습니다. 여기에 코드가

구현 되어있지는 않습니다.

hello_fn.c를 컴파일하면 여기도

hello.h 파일을 인클루드하고 있는데

헤더파일이 이 두 파일 main.c과

hello_fn.c 사이의 매개체가 됩니다.

 

그럼 컴파일을 해보겠습니다.

 

첫번째 방법은 모두 한꺼번에

컴파일 하는 방법입니다.

gcc -Wall main.c hello_fn.c -o myhello

이렇게 하면 실행파일까지 만들어집니다.

컴파일 + 링크까지 완료됩니다.

 

개별적으로 컴파일하는 방법은

아래와 같습니다.

gcc -Wall -c main.c
gcc -Wall -c hello_fn.c 
gcc main.o hello_fn.o -o program

-c 옵션으로 main.c 를 컴파일하면

main.o 파일이 생성됩니다.

이것만으로 실행이 되지 않습니다.

hello.h 파일에 있는 프로토타입이

구현되어 있는 hello_fn.c 코드를

컴파일하고 이 두개의 오브젝트 파일을

링크를 한 후에 실행파일을 만들 수 있습니다.

 

*개별적으로 컴파일하는 장점은

수정이 필요한 파일만 컴파일하면

되기 때문에 하나를 수정하기 위해

모든 소스파일을 다시 컴파일 할

필요가 없습니다. 하지만 수정하는 파일이

종속성(dependency)를 변경한다던가

하는 경우는 다른 파일을 함께

컴파일 해야하는 경우도 있습니다.

 

- 그냥 gcc main.c 이렇게 컴파일을

시도하면 아래와 같은 메시지를 받습니다.

/usr/bin/ld: /tmp/cc4rlx5A.o: in function `main':
main.c:(.text+0x10): undefined reference to `hello'
collect2: error: ld returned 1 exit status

ld 는 gcc가 호출하는 링커입니다.

링커는 실행파일을 만들 때

hello 라는 식별자의 참조가

확인되지 않았다는 메시지를 출력합니다.

 

hello 함수를 구현한 오브젝트 코드가

없기 때문에 링크를 완료할 수 없습니다.

 

GCC 의 에러메시지는 상당히 자세하기

때문에 메시지만 잘 읽어봐도 답이

나오는 경우가 많습니다.

물론 대부분의 에러메시지는

stackoverflow 에 검색하면

얼추 답이 나옵니다.

 

C언어 같이 인기있는 언어는

수백만개의 질문들에 유사한

내용이 등록되어 있기 때문에

오류메시지를 복사해서

인터넷에 찾는 수고 정도로도

웬만한 오류는 해결이 가능할겁니다.

 

gcc 는 편의를 의해서 바로

링커까지 호출하지만 -c 옵션을

사용해서 하나씩 컴파일 하며

단계적으로 컴파일 과정을

확인하면 컴파일에 대하여

좀더 이해를 할 수 있을 겁니다.

 

gcc 컴파일러에는 수많은 옵션이

있는데 그것들을 항상 사용하지는

않을 것이기 때문에 당장 모든 것을

외우거나 그럴 필요는 없습니다.

 

*중요한 것은 이것 입니다.

 

소스코드(텍스트파일)

-> 오브젝트코드(바이너리)

-> 실행파일(바이너리)

 

파일이 .c 확장자에서 .o

그리고 최종실행 파일 (a.out)이

나올 때 까지 두번이나 변환이 됩니다.

 

이 과정에서 컴파일러가 운영체제나

CPU 등 플랫폼에 적합한 기계어에

맞춰서 빌드를 할 수 있습니다.

 

크로스플랫폼이라는 어려운 말을

많이 하는데 소스코드는 똑같거나

최대한 비슷하게 작성해도

어느 환경에서나 빌드만 하면

실행할 수 있는 시스템을 말합니다.

 

크로스플랫폼은 또 다른 이야기이지만

빌드과정에서 한번쯤 생각해볼 문제입니다.

 

이것을 위해서 자바나 C#등

다른 언어들은 런타임에

가상머신을 사용하고 있습니다.

 

반면 C언어는 저수준이라 플랫폼

종속적이어야 하지만 다양한 환경에서

빌드가 가능하기 때문에  크로스플랫폼에

적합한 언어에 속합니다.

 

단지 일반 사용자들이 GCC로 빌드를

하는 것은 무리니까 해당 플랫폼에 맞게

배포판을 만드는 것은 프로그래머가

해야 할 일입니다. 적어도 어떻게 빌드하는지

정도는 알아듣게 설명할 수 있어야 합니다.

공유하기

facebook twitter kakaoTalk kakaostory naver band