목차
컴파일이란?
컴파일 과정
1. 전처리 과정
2. 컴파일 과정
3. 어셈블러 과정
4. 링킹 과정
+ 컴파일타임과 런타임
C언어에서 소스코드가 실행파일이 되어 실행되기까지의 과정은 어떻게 될까??
그냥 소스 코드를 실행 시키면 뿅! 자동으로 실행 파일이 되는 걸까??
전혀 아니다..! 이번 포스팅에서는 C언어의 컴파일 과정을 알아보려 한다.
📌 컴파일이란?
C, C++, Java같은 언어로 작성하는 소스 코드는 우리 눈에 컴퓨터 언어같아보일지라도 진정 컴퓨터 언어는 아니다. 우리의 소스 코드는 컴퓨터가 절대 이해할 수 없다. 컴퓨터는 0과 1로 이루어진 기계어만 이해할 수 있다. 따라서 우리의 소스 코드를 기계어로 번역하여 전달해 주어야 하는데, 이 번역 과정을 바로 컴파일
이라고 한다.
쉽게 말해,
컴파일
이란 우리가 우리의 언어로 작성한 소스 코드를 CPU가 이해할 수 있는 기계어로 번역하는 작업을 말한다.
컴파일을 수행해주는 것은 컴파일러다.
컴파일 과정
컴파일 과정을 쉽게 요약하면 다음과 같다.
- 전처리 과정
- 컴파일 과정
- 어셈블리 과정
- 링킹 과정
이렇게 크게 네가지 단계로 나누어 진다.
과정을 하나하나 살펴보자.
1. 전처리 과정
전처리 과정은 본격적으로 컴파일 하기 전 처리할 작업들을 담당하는 과정이다.
다음과 같은 과정을 수행한다.
- 주석 제거 : 소스 코드에서 주석을 제거한다.
- 헤더 파일 삽입, 라이브러리 포함(
#include
등) : 헤더 파일에 있는 모든 내용을 복사해서 소스 코드에 삽입한다. - 프로그래밍의 편의를 위해 작성된 매크로 변환 :
#define
에 정의된 매크로 이름을 찾아서 정의한 값으로 치환해준다. - 컴파일 할 영역 명시(#if, #ifdef, ...)
전처리 과정을 직접 눈으로 살펴보자.
먼저 실행기를 켜서
vi hello.c
를 입력하고 다음과 같은 코드를 작성해준다.
#include <stdio.h>
int main(){
printf("hello world!\n");
return 0;
}
이후
gcc -E hello.c -o hello.i
를 입력해준다.
이 구절은 gcc라는 컴파일러로 전처리를 하고, 그 결과물의 이름을 hello.i로 저장하겠다는 소리다.
이후 hello.i를 확인해보면
이런 코드가 잔뜩 들어가있는 것을 볼 수 있다.
그리고 마지막에
이렇게 원래의 코드가 나온다.
위의 복잡한 코드들은 헤더에 있는 stdio.h
의 소스코드들을 모두 가져온 것이다.
결과물을 보면 아시겠지만 전처리가 완료 되어도 여전히 소스 코드의 형태임을 알 수 있다.
아직까진 전혀 기계어로 번역이 되지 않았음을 알 수 있다.
2. 컴파일 과정
이제 전처리가 완료된 코드를 저급 언어(어셈블리어)로 변환하는 과정을 거친다.
어셈블리어는 CPU, 언어마다 다른 형태를 갖고 있다.
gcc에 -S 옵션을 주게 되면 컴파일 된 *.s 확장자 파일이 생긴다.
gcc -S hello.i -o hello.s
hello.s 파일을 살펴보면
이렇게 어셈블리어로 변환된 모습을 확인할 수 있다.
3. 어셈블러 과정
이제 어셈블리어를 기계어로 번역해주어야 한다.
* 이때, 목적파일(object)은 실행파일이 아니다. 링킹 과정을 거쳐야 실행파일이 됨에 유의하자.
gcc -o hello.o hello.s
이렇게 만들어진 hello.o 파일을 그냥 열어보면
도통 무슨 소린지 알 수 없는 외계어가 우릴 반긴다.
이런 파일은 xxd
라는 프로그램으로 열어서 볼 수 있다.
열어서 보면 이런 모양새를 볼 수 있음.
4. 링킹 과정
이제 마지막 단계!
각기 다른 목적 코드와 라이브러리 파일들을 하나의 실행코드로 묶어주는 작업이 바로 링킹 단계이다.
이렇게 링킹 과정까지 끝나면 소스코드가 실행파일로 거듭난다.
➕ 컴파일타임과 런타임
링커를 통해 실행파일이 만들어지는 순간까지를 컴파일타임이라고 부르고,
실행파일이 실행된 후의 시간을 런타임이라고 부른다.
컴퓨터는 내 똥같은 코드를 이해해주느라 엄청 많은 노력을 들이고 있다는 사실을 알아봤다. (엄청 빨리 해내지만..)
따봉 컴퓨터야 고마워!!
-끗-