개요
C++ 소스 코드가 컴퓨터가 이해할 수 있는 이진(Binary) 실행 파일로 변환되기까지는 여러 단계의 정교한 과정이 필요합니다. 전체적인 흐름은 다음과 같습니다.
소스 코드 (.cpp, .h) -> 전처리 (Preprocessing) -> 컴파일 (Compilation) -> 어셈블리 (Assembly) -> 링킹 (Linking) -> 실행 파일 (.exe, .out)
1. 전처리 (Preprocessing)
전처리는 실제 컴파일이 시작되기 전, 소스 코드 수준에서 텍스트를 조작하는 단계입니다. 전처리기(Preprocessor)는 #으로 시작하는 지시문을 해석하여 소스 코드를 변환합니다. 이 과정에서 주석은 제거되고, 매크로가 확장되며, 필요한 파일들이 포함됩니다.
1.1 주요 전처리 지시문
- 파일 포함 (#include): 외부 헤더 파일의 내용을 해당 위치에 그대로 복사합니다.
#include <iostream> // 표준 라이브러리 경로에서 검색 #include "my_header.h" // 현재 프로젝트 디렉토리에서 먼저 검색 - 매크로 정의 (#define, #undef): 특정 식별자를 문자열이나 식으로 치환합니다.
#define LIMIT 100 #define AREA(r) ((r) * (r) * 3.14)전처리기는 코드 내의
LIMIT를100으로 모두 바꿉니다.#undef는 정의된 매크로를 해제할 때 사용합니다. - 조건부 컴파일 (#ifdef, #ifndef, #endif): 특정 조건에 따라 코드의 일부를 컴파일에 포함하거나 제외합니다. 주로 헤더 파일의 중복 포함을 방지하는 '헤더 가드'로 자주 사용됩니다.
#ifndef HEADER_FILE_H #define HEADER_FILE_H // 헤더 내용 #endif
2. 컴파일 (Compilation)
컴파일 단계에서는 전처리가 완료된 소스 코드를 분석하여 타겟 아키텍처에 맞는 어셈블리 코드로 변환합니다. 이 과정은 크게 어휘 분석, 구문 분석, 의미 분석, 최적화 단계로 나뉩니다. 컴파일러는 언어의 문법을 체크하고 코드를 효율적으로 재구성하여 어셈블리어 파일(.s)을 생성합니다.
3. 어셈블리 (Assembly)
어셈블러(Assembler)는 앞선 단계에서 생성된 어셈블리 코드를 기계어(이진 코드)로 변환하여 목적 파일(Object File)을 생성합니다. 윈도우에서는 .obj, 리눅스/유닉스에서는 .o 확장자를 가집니다.
목적 파일은 크게 두 부분으로 구성됩니다.
- 코드 섹션 (.text): 실제 실행될 기계어 명령어가 들어있습니다.
- 데이터 섹션 (.data, .bss): 전역 변수나 정적 변수가 저장되는 공간입니다.
4. 링킹 (Linking)
여러 개의 소스 파일로 구성된 프로젝트에서 각 파일은 독립적으로 컴파일되어 여러 개의 목적 파일을 만듭니다. 링커(Linker)는 이 목적 파일들과 라이브러리 파일들을 하나로 묶어 실행 가능한 하나의 파일로 완성합니다. 이때 외부 참조(다른 파일에 정의된 함수나 변수 사용)를 실제 주소와 연결하는 작업을 수행합니다.
4.1 링킹의 종류
- 정적 링킹 (Static Linking): 라이브러리의 코드를 실행 파일 안에 직접 복사하여 넣습니다. 실행 파일의 크기는 커지지만 별도의 라이브러리 파일 없이 실행 가능합니다.
- 동적 링킹 (Dynamic Linking): 라이브러리 코드를 포함하지 않고, 실행 시점에 라이브러리(Windows의
.dll, Linux의.so)를 로드하여 사용하도록 연결 정보만 기록합니다. 메모리 효율이 좋고 라이브러리 업데이트가 용이합니다.
5. 실제 빌드 과정 예시
main.cpp 소스 파일을 기반으로 단계별 명령어를 살펴보겠습니다.
5.1 단계별 명령어 (GCC/G++ 기준)
// 1. 전처리: main.i 생성
g++ -E main.cpp -o main.i
// 2. 컴파일: main.s (어셈블리 코드) 생성
g++ -S main.i -o main.s
// 3. 어셈블리: main.o (목적 파일) 생성
g++ -c main.s -o main.o
// 4. 링킹: 최종 실행 파일 생성
g++ main.o -o my_app
최종적으로 생성된 my_app(또는 my_app.exe) 파일은 운영체제에 의해 메모리에 로드되어 실행될 수 있는 완성된 상태가 됩니다.