본문 바로가기

Cpp/Story

C++에서 Compiler는 어떻게 동작할까?

컴파일러(Compiler)란? : 번역하는 프로그램. 책을 통째로 번역하는 것과 같다.

사람은 C언어를 사용하고, 컴퓨터는 이진법 언어(Binary code, Hex code, object code, machine code 등)를 사용하기에 서로의 언어를 이해하지 못합니다. 그래서 이를 해결하기 위해 필요한 것이 바로 Compiler입니다. Compiler는 C언어를 기계가 이해할 수 있도록 이진법 언어로 번역합니다. 이런 과정을 거쳐 사람은 C언어를 통해 바로 .exe와 같은 프로그램을 생성합니다.

 

C++의 Compiler인 g++를 예시로 하여 그림으로 표현해 보았습니다. 한 단계씩 설명해 보겠습니다.

 

 

 

 

C++ 코드를 g++ Compiler에서 -save-temps 옵션을 통해 만들어진 실제 결과물들을 나타낸 자료 입니다.

 

Preprocessing

사람이 작성한 코드를 전처리 합니다. 이때 #include, #define 했던 것들이 코드에 실제로 적용되게 됩니다. 예시 그림을 보시면, Task.cpp는 12줄인데, Task.ii는 3만 줄이 넘어갑니다. 이는 #include <iostream>이 3만 줄이 넘어가기 때문입니다.

전처리 작업이 끝나면. 확장자를 ii로 바꿔줍니다. 이는 더 이상 전처리 하지 않겠다는 표시입니다. 그림에 있는 Task.ii와 Task.cpp를 비교해 보면, #define PLUS + 가 적용되어, sum 함수가 변화되었음을 알 수 있습니다.

Complication

전처리 작업이 끝나면, 본격적으로 코드를 어셈블리(assembly) 코드로 전환합니다. 어셈블리 코드 특징은, 실제 CPU에 있는 하드웨어 명령어와 거의 1:1을 이루는 코드들로 되어 있다는 것입니다. 그렇기 때문에 거의 CPU가 읽기 직전까지 왔다고 생각하면 됩니다.

Assembly

어셈블리 언어를 binary(hex) 코드로 만들어 .o(obj file)에 저장합니다. 이 파일은 CPU가 이해할 수 있는 언어인 machine code로 작성되었으며, 이제는 기계가 읽을 수 있습니다. 하지만, 작성된 코드들이 완전히 하나로 합쳐진 상태가 아닙니다. 코드들이 여러 파일에 걸쳐서 작성되었다면, 하나로 합칠 필요가 있습니다,

Linking

obj file들을 이제 하나로 모으고, Library도 붙이고, OS환경에 맞게 동작하도록 정보를 모으고 모아 .exe(executable file) 파일을 만듭니다. 이 파일은 이제 완전히 프로그래머가 작성한 코드대로 동작합니다. 여기가 사실상 최종 목적지입니다.

(exe 파일은 사실 windows OS의 주된 형식이고, linux의 경우는 보통 .out 확장자를 붙이나, 다른 확장자여도 실행 가능합니다.)

 

 * 이 자료를 준비하면서 놀랐던 사실이 있습니다, 저는 단순히 obj파일이 합쳐져 exe가 만들어진다 생각했고, 그로 인해 obj의 용량들의 합이 exe와 비슷하다 생각했습니다만, 실제 실험은 단일 cpp코드로 진행했을 때, obj 파일이 약 900 Bytes, exe가 57.1KB로, 50배가 넘는 용량 차이를 확인했습니다. 최적화 여부를 감안하더라도, header파일이 없고, 다른 obj파일도 없는데 꽤 많은 차이를 보이고 있습니다.

 

test.exe와 text.o는 같은 Compile 과정에서 나온 파일입니다. 용량이 50배 넘게 차이 나는 것을 확인할 수 있습니다.

 

제가 예측하지 못한 Linking 작업에는 다음과 같은 작업이 들어가 있었습니다.

 

Startup code.

이런 내용은 obj file에는 없는 내용입니다. 프로그램의 환경을 설정하고 실행을 준비하는 코드입니다.

 

 

Debugging information.

Error가 발생했을 때 어떠한 사유로 죽었는지 메시지가 담긴 부분입니다. 저는 Mingw를 활용해서 g++로 컴파일 했는데, 메시지를 읽어 보면 관련 Mingw-w64 runtime failure도 들어가 있음을 확인할 수 있습니다.

 

 

 

 

Relocation information.

프로그램이 Load 될 때 코드와 데이터를 메모리에 재배치할 위치를 운영체제(OS)에가 알려줍니다. PSEUDO_RELOC로 이루어진 글자들이 보입니다. 나머지 글자들에서도 이 프로그램의 스펙에 대해서 힌트를 담고 있습니다.

 

 

이런 기능을 가진 코드들이 exe에 포함되어 있어, 동일할 것이라 생각했던 obj와 exe 간의 용량 차이가 있었던 것입니다.

 

Execute

.exe가 된 code는 이제 실행할 수 있게 되었습니다. 사람이 이해할(?) 수 있는 C++ 언어에서 CPU가 이해할 수 있는 binary로 Compile 되었습니다.

 


마치며

Compiler에 대한 이해가 있으셔야 CPU가 어떻게 프로그램을 이해하고 실행하는지를 알 수 있으며, 프로그래밍을 하는데 더 최적화에 신경을 쓸 수 있습니다. 현대의 Compiler는 스스로 최적화를 해주는 정도가 수준이 높아 신경을 덜 쓰게 되었지만 그래도 너무 언어에만 집중하기 보다는 Compiler의 동작 원리를 생각하면서 프로그래밍을 해보시길 바랍니다. 감사합니다.

'Cpp > Story' 카테고리의 다른 글

C++ 이란?  (0) 2023.02.05