컴파일은 우리가 열심히 짠 소스코드를 CPU가 알아먹을 수 있도록 기계어로 변환하는 과정이다. 크게 4단계로 나뉜다. 전처리, 컴파일, 어셈블, 링크

  1. 전처리 전처리기(preprocessor)가 컴파일을 하기 전에 코드를 적정 상태로 준비한다. 이 적정 상태가 무엇인지 구체적으로 알아보자면 #include 구문을 만나면 해당하는 헤더 파일을 찾아 내용을 삽입 한다. 주석을 삭제하고 #define 매크로를 실행한다. *.c가 *.i로 바뀐다.
  2. 컴파일 이렇게 전처리된 파일을 어셈블리어로 변환한다. 여기서 최적화를 수행하기 때문에 *.i(전처리)파일보다 훨씬 코드가 짧아진다. 확장자명이 *.s로 바뀐다. 컴파일 과정은 크게 3단계로 나뉜다. Front end, Middle end, Back end이다.Front end 여기서는 문법과 관련되어서 처리를 한다.
  3. C코드를 토큰 단위로 나눈다.
  4. 토큰들로 파스 트리(Parse Tree)를 만들며 문법 오류를 검출한다.
  5. 문법적 오류는 없을지 모르겠지만 의미상으로 오류가 있을 수 있다. 예를 들어 Type불일치나 함수 매개변수를 잘못 사용한 경우가 있겠다.
  6. Middle end에 넘겨주기 위한 GIMPLE 트리를 생성한다. 흠. 문법은 대충 알겠는데 GIMPLE 트리는 무엇일까? 이건 C언어가 아닌 다른 C++이나 Java와 같은 언어들이 각 언어에 맞게 처리되는 공통된 중간 표현 (intermediate representation)이다. 이걸 사용함으로써 언어의 종속성을 벗어날 수 있다.Middle end 여기서는 아키텍처에 대해 대해 처리를 한다. 여기서 아키텍처는 CPU 아키텍처를 말한다. 먼저 Front end에서 넘겨받은 GIMPLE Tree를 SAA(Static Single Assignment)로 변환한다. arm, x86, x64든 뭐든 어떤 아키텍처에서도 실행할 수 있도록 처리해준다. 마지막으로 Back end에서 사용하기 위해 RTL(Register Transfer Language)구조로 변환한다.Back end Middle end에서는 아키텍처의 종속성으로부터 벗어나 최적화를 진행했다면(비종속화) 여기서는 아키텍처의 특징에 따라 최적화를 한다. 다시 아키텍처 종속적으로 돌아가는 것이다. 같은 기능을 수행하는 명령어여도 아키텍처 별로 효율적인 명령어로 대체 하여 최적화를 한다.
  7. 어셈블 어셈블리어를 기계어로 변환한다. 확장자명이 *.o로 바뀐다. 이때부터 사람이 읽을 수 없는 언어가 된다. 이런 파일들을 Object File이라고 한다.
  8. 링크 *.o파일과 라이브러리를 결합해서 하나의 실행파일로 만든다. 이 파일이 진짜 OS가 이해하고 실행할 수 있는 파일이다. *.exe파일이 만들어진다. 이 파일을 실행하면 RAM에 로드되어 시스템에서 동작하게 된다. 컴파일러 vs 인터프리터 컴파일러 인터프리터컴파일 시점에 전부 기계어로 변환런타임에 한 문장씩 변환컴파일 시점에 코드의 에러를 감지런타임 중 에러를 발견함.컴파일 시간은 오래 걸리나 실행이 빠름프로그램 시작은 빠르나 실행 속도가 느림링킹에 필요한 Object Code를 생성해서 많은 메모리 필요Object Code따윈 만들지 않음C, C++, Java, TypeScriptJavaScript, Python, Ruby JavaScript는 인터프리터인가? 맞다. 하지만 크롬 V8엔진 또는 Node.js에서 JIT(Just in Time) 컴파일 방식으로 JavaScript를 처리한다. JIT방신은 인터프리터로 컴파일해서 Byte Code로 만들고 이 Byte Code를 캐싱하는 등 최적화해서 컴파일할 때 다시 참조하는 방식으로, 인터프리터보다 더 빠른 속도로 실행 가능하다.

    1. C코드를 토큰 단위로 나눈다.
    1. 토큰들로 파스 트리(Parse Tree)를 만들며 문법 오류를 검출한다.
    1. 문법적 오류는 없을지 모르겠지만 의미상으로 오류가 있을 수 있다. 예를 들어 Type불일치나 함수 매개변수를 잘못 사용한 경우가 있겠다.
    1. Middle end에 넘겨주기 위한 GIMPLE 트리를 생성한다.