C언어 컴파일 과정
C 언어 컴파일 과정
C 언어로 작성된 소스 코드는 여러 단계를 거쳐 컴퓨터가 이해할 수 있는 실행 가능한 바이너리 파일로 변환됩니다. 이 과정은 일반적으로 다음과 같은 4단계로 나뉘며, 대표적인 컴파일러인 `gcc`를 기준으로 설명합니다.
위와 같은 단계를 통해 생성된 실행 파일은 메모리(RAM)에 적재되어 운영체제 상에서 실제로 동작하게 됩니다.
1. 전처리 (Preprocessing)
- `#include` 지시문에 따라 헤더 파일의 내용을 삽입합니다. - `#define`, `#ifdef`, `#ifndef` 등 매크로를 해석하고 코드에 적용합니다. - 주석 제거 및 조건부 컴파일 처리도 이 과정에서 수행됩니다.
→ 전처리 결과는 확장자가 `.i`인 중간 코드로 저장됩니다.
2. 컴파일 (Compilation)
전처리된 코드를 기반으로 어셈블리 코드로 변환하는 단계이며, 내부적으로는 세부적인 세 단계로 구성됩니다:
- 전단부 (Front-end):
- 어휘 분석(Lexical Analysis), 구문 분석(Syntax Analysis), 의미 분석(Semantic Analysis)을 수행합니다. - 주로 문법 오류를 탐지하는 역할을 합니다.
- 중단부 (Middle-end):
- SSA(Static Single Assignment) 형태로 변환 후 다양한 최적화를 수행합니다. - 실행 성능을 개선하기 위한 중간 코드 최적화가 이루어집니다.
- 후단부 (Back-end):
- 대상 시스템 아키텍처에 맞게 최적화된 어셈블리 코드로 변환합니다. - 불필요한 연산 제거, 명령어 재배치 등을 통해 실행 효율을 극대화합니다.
→ 결과는 `.s` 확장자의 어셈블리 코드 파일입니다.
3. 어셈블 (Assembling)
어셈블리 코드를 기계어(Object Code)로 번역하는 단계입니다.
- 어셈블러는 `.s` 파일을 받아 `.o` 확장자의 오브젝트 파일(Object File)로 변환합니다. - 생성된 오브젝트 파일은 **ELF(Executable and Linkable Format)** 구조를 따르며, 명령어와 데이터가 일정한 규칙으로 정렬되어 있습니다. - 이 구조는 나중에 링커가 여러 오브젝트 파일과 라이브러리를 효과적으로 결합할 수 있도록 돕습니다.
4. 링킹 (Linking)
- 여러 개의 오브젝트 파일(.o)과 라이브러리(.lib/.a)를 결합하여 하나의 실행 파일을 만듭니다. - 함수 호출, 전역 변수 참조 등 서로 연결되지 않은 심볼(Symbol)을 해결합니다. - 동적 라이브러리를 사용하는 경우 해당 정보를 실행 파일에 포함시킵니다.
→ 최종 결과물은 확장자가 `.out`, `.exe`, 또는 사용자 정의 이름의 실행 파일입니다.
요약
단계 | 입력 파일 | 출력 파일 | 주요 도구 |
---|---|---|---|
전처리 | hello.c | hello.i | cpp |
컴파일 | hello.i | hello.s | cc1 |
어셈블 | hello.s | hello.o | as |
링킹 | hello.o | a.out (또는 지정 이름) | ld |
관련 링크
- [GCC 공식 문서](https://gcc.gnu.org/)
- [ELF 포맷 위키백과](https://ko.wikipedia.org/wiki/실행_가능_및_연결_형식)