MJay

C 프로그래밍 : C 컴파일러 [gcc](Scrap) 본문

Linux

C 프로그래밍 : C 컴파일러 [gcc](Scrap)

MJSon 2017. 7. 16. 17:50


C 프로그래밍 C 컴파일러 : gcc



C 코드를 작성하고 나서 컴파일을 하려 할때 기본적을 gcc 컴파일러를 많이 쓰게 된다. 

GNU에서 제공하는 gcc 컴파일러를 이용하여 UNIX 시스템에서 컴파일하고, 목적파일을 생성하며, 라이브러리를 링크 시키는 과정을 알아본다. 


유닉스에서 C 프로그래밍을 하면서 부터 이런 컴파일러에 대한 개념이 많이 부족하여 공부할 겸 포스팅을 작성하게 되었으며, 이 포스팅의 내용은 [실무 전문가가 짚어주는 UNIX] 란 서적을 참고하였다. 



  gcc

기본적으로 gcc 를 이용하여 컴파일을 해보자. 


우선 가장 기본적인 "Hello world !!" 를 작성하고 gcc를 이용하여 컴파일 해본다. 


hello.c

#include <stdio.h>

int main( void ){

    printf("HELLO WORLD !! \n");

    return 0;

}

[컴파일] gcc hello.c

[실행] ./a.out



gcc 컴파일러로 hello.c 파일을 컴파일하면, a.out이라는 실행 파일이 생성되고, 생성된 a.out 파일을 이용하여 실행 할 수 있다. 
컴파일 과정에서 실행파일의 이름을 지정해 주지 않으면 기본적으로 a.out 이라는 이름으로 실행파일이 생성된다. 


  컴파일 결과 파일 지정 [ -o 옵션 ]
컴파일 결과로 생성된 모든 실행파일의 이름이 a.out 으로 생성된다면 큰 문제가 발생한다. hello.c 같은 간단한 프로그램 같은 경우에는 하나의 소스만 컴파일 한다지만, 하나의 디렉토리에서 여러개의 소스를 컴파일 하게 되면 결국 남는 것은 a.out 하나의 실행파일만 생길 것이기 때문이다. 
이런 문제를 해결하기 위해서 gcc 에서는 -o 옵션을 제공하여 컴파일 과정에서 만들어지는 실행 파일의 이름을 수정 할 수 있도록 지원한다. 
gcc [옵션] [실행파일 이름] [컴파일할 파일]
gcc -o hello hello.c 




  목적(Object) 파일 생성 [ -c 옵션 ]

목적파일은 컴퓨터가 이해할 수 있도록 구성되어 있는 기계어로 저장된 파일이다. 

C컴파일러는 정확하게 말해서 그 자체가 컴파일러라기보다는 소스파일을 읽어 실행파일을 만드는데 필요한 각 단계별 명령어를 호출하는 연결자라고 할 수 있다. 즉, gcc 와 같은 C 컴파일러는 입력받은 소스파일을 기반으로 cpp, cc1, as, ld 명령어를 순차적으로 호출하여 실행파일을 만든다. 


실행파일이 만들어지기까지 소스파일이 거치는 단계별 과정은 다음과 같다. 


1. 전처리 단계 - 전처리기 (cpp)

: #include, #define 등 #으로 시작하는 문법 사항이 적절히 전처리된 C 언어 소스파일 생성

2. 컴파일 단계 - 컴파일러 (cc1) 

: C 언어 소스파일은 컴파일 과정을 거쳐 어셈블리 소스 파일이 됨

3. 어셈블 단계 - 어셈블러 (as)

:어셈블리 소스 파일은 어셈블 과정을 거쳐 목적 파일이 됨 

4. 링크 단계 - 링커 (ld)

: 목적 코드는 라이브러리와 링크되어 실행 가능한 파일이 됨


-c 옵션에 의해 만드어진 목적 파일은 여러개의 C 소스 파일로 이루어진 프로그램을 컴파일할 때 그 필요성이 부각된다. 
다음과 같이 여러개의 소스파일로 이루어진 프로그램을 통하여 왜 목적 파일이 필요한지 알아본다. 

naddru.h
#include <stdio.h>
void fun1();
void fun2();

main.c
#include "naddru.h"
int main( void ){
    fun1();
    fun2();
    return 0;
}

fun1.c
#include <stdio.h>
void fun1(){
    printf("fun1 !! \n");
}

fun2.c
#include <stdio.h>
void fun2(){
    printf("fun2 !! \n");
}

이런 여러개의 파일을 -o 옵션을 이용하여 실행 파일을 생성하면 앞선 4단계를 거쳐서 새로은 실행 파일을 생성하게 된다. 

gcc -o main main.c fun1.c fun2.c



그렇다면, -c 옵션을 이용하여 목적파일을 통하여 실행파일을 만들어 본다. 


gcc -c main.c

gcc -c fun1.c

gcc -c fun2.c

gcc -o main main.o fun1.o fun2.o



사용자 입장에서 보면 입력 단계가 적은 첫번째 단계가 더욱 효율적으로 보인다. 
그러나, 만약 fun1.c 의 소스가 수정 되거나 변경되었을 경우는 이야기가 다르다. 
-o 옵션을 이용하여 컴파일을 진행하면, main.c 와 fun2.c 와 같이 변경사항이 없는 소스에 대해서는 필요치 않은 컴파일을 진행하였다. 

내부적으로는 모든 파일을 재컴파일 하는 것보다 수정된 파일만 컴파일 하는 것이 훨씬 효율적이다. 
목적 파일을 이용하면, 수정된 파일만 재컴파일 하고, 기존 파일과 링크만 시킴으로써 내부적으로 비효율적인 일을 하지 않을 수 있다. 

그리고 나중에 Makefile 을 이용하게 되면, 입력에 따르는 부담도 줄일 수 있다.