카테고리 없음

[기술 질문]#8_컴파일러, Compiler

Hardii2 2023. 2. 5. 20:48

 

[기술 질문] #8_컴파일러, Compiler

 

컴파일러에 대해 알아보겠습니다.

 


 

Overview

 

  1. 개념
  2. 컴파일 과정
  3. 컴파일러 vs 인터프리터
  4. C# vs C++
  5. 게임 프로그래밍의 C++ 사용 이유

 

#1. 개념

1. 컴파일러??

  • 컴파일(Compile)은 주어진 언어로 작성된 컴퓨터 프로그램을 다른 언어의 동등한 프로그램으로 변환하는 프로세스입니다.
  • 일반적으로, 컴파일러는 고급 언어로 작성된 코드들을 컴퓨터 언어로 변환하는 작업을 일컫습니다.

 

2. 인터프리터??

  • 인터프리터는 고급 언어로 작성된 원시 코드를 바로 실행하는 프로그램 또는 환경을 의미합니다.
  • 일반적으로, 인터프리터는 고급언어로 작성된 원시 코드 명령어들을 한 번에 한 줄씩 읽어가며 실행하는 프로그램입니다. 
  • 소스 코드를 직접 실행하거나, 소스 코드를 다른 효율적인 중간 코드로 변환하고, 변환한 것을 바로 실행합니다. 더불어, 인터프리터 시스템의 일부인 컴파일러가 만든 미리 컴파일된 저장 코드의 실행을 호출하기도 합니다.

 

#2. 컴파일 과정

1.  전처리

  • 전처리기를 통해 소스 코드 파일(*.c)을 전처리된 소스 코드 파일(*. i)로 변환하는 과정입니다. 이 과정에서 전처리기는 세 가지 작업을 수행합니다-주석 제거, 헤더 파일 삽입, 메크로 치환 및 적용.
  • 주석 제거는 말 그대로 소스 코드에 작성된 주석들을 모두 제거합니다.
  • 헤더 파일 삽입 작업은 "#include" 지시문을 만나 해당 헤더파일을 찾아 모든 내용을 복사해서 소스코드에 삽입하는 작업입니다. 헤더 파일에서 선언된 함수 원형은 후에 링킹 과정을 통해 실제 함수가 정의되어 있는 오브젝트 파일과 결합합니다.
  • 메크로 치환 및 적용 작업은 "#define" 지시문에서 정의한 메크로를 저장하고, 치환하는 작업을 수행합니다.

 

2. 컴파일

  • 전처리된 소스 코드 파일(*.i)를 어셈블리어 파일(*. s)로 변환합니다. 일반적으로, 언어의 문법 검사와 함께 Data영역의 메모리 할당을 수행합니다.
  • 컴파일러는 총 세 단계로 구성되어 있습니다-프런트 엔드, 미들 엔드, 백 엔드.
  • 프론트 엔드언어 종속적인 부분을 처리합니다. 소스 코드가 해당 언어의 문법에 맞게 작성되었는지 확인하고, 어휘 분석(lexical analysis), 구문 분석(syntax analysis), 의미 분석(semantic analysis) 등의 과정을 거쳐 언어 종속적인 문제를 해결합니다. 이 과정을 통해 중간 표현(Intermediate Representation, IR)인 일반적으로 GIMPLE 또는 SSA(Static Single Assignment) 트리 등의 형태로 소스 코드를 변환합니다.
  • 미들 엔드아키텍처 비종속적인 최적화를 수행합니다. 아키텍처 비종속적인 최적화란, CPU 아키텍처에 상관없이 적용할 수 있는 최적화입니다. 미들 엔드는 프런트 엔드에서 생성된 중간 표현을 바탕으로 다양한 최적화 기법을 적용하여 프로그램의 성능을 향상시킵니다.
  • 백 엔드아키텍처 종속적인 최적화를 수행하고, 최종 어셈블리 코드를 생성합니다. 아키텍처 종속적인 최적화 작업은 각 아키텍처의 특성에 맞는 최적화를 수행하는 과정입니다. 이를 통해 CPU 아키텍처에 특화된 명령어를 생성하는 등의 작업을 수행합니다. 백 엔드에서는 미들 엔드에서 처리된 중간 표현을 바탕 어셈블리 코드로 변환하고, 이를 바이너리 코드로 컴파일하여 실행 파일을 생성합니다.

 

3. 어셈블리

  •  어셈블러를 통해 어셈블리어 파일(*. s)을 오브젝트 파일(*. obj)로 변환합니다.

 

4. 링킹

  • 링커는 오브젝트 파일들을 묶어 실행 파일로 만드는 작업을 수행합니다.
  • 정확히 말해서, 링커는 이 과정에서 오브젝트 파일들과 프로그램에서 사용하는 라이브러리 파일들을 링크하여 하나의 실행 파일을 만듭니다.

 

#3. 컴파일러 vs 인터프리터

1. 컴파일러 특징

  1. 컴파일러는 원시 코드로 작성된 프로그램 전체를 목적(오브젝트) 프로그램으로 번역한 후, 링킹 작업을 통해 컴퓨터에서 실행 가능한 프로그램을 생성합니다.
  2. 여러 과정들을 거쳐야 하기 때문에, 비교적 많은 시간이 소모되지만, 한번 변역 된 프로그램은 다음 사용 시 다시 번역할 필요가 없습니다.

 

2. 인터프리터 특징

  1. 인터프리터는 원시 코드를 한 번에 한 줄씩 번역하고, 변역과 동시에 프로그램을 한 줄 단위로 그 즉시 실행시키는 프로그램입니다.
  2. 인터프리터는 변역 속도가 빠르지만, 매 실행마다 번역 작업을 수행합니다.

 

3. 컴파일러와 인터프리터의 차이점

구분 컴파일러 인터프리터
번역 단위 전체
목적 프로그램 생성 비 생성
실행 속도 첫 실행시 느림, 이후에 빠름 빠르지만, 매 실행 마다 번역 수행
번역 성능 느림 빠름
언어 C, JAVA Python, BASIC, LISP, ...etc

 

#4. C++ vs C#

1. C++과 C#의 차이점

구분 C++ C#
컴파일 결과 어셈블리어(저수준) 중간 수준 언어
값 타입 정적 할당한 모든 타입 기본 타입, 구조체
참조 타입 동적 할당한 모든 타입 클래스, 객체
동적 할당 포인터 변수 참조 타입(클래스 객체)
메모리 해제 수동 자동(가비지 컬렉션)

 

2. 컴파일 결과

  • C++로 작성한 코드는 컴파일 과정을 통해 어셈블리어로 작성된 복적 파일이 생성됩니다.
  • C#으로 작성한 코드는 운영체제 위에. NET이라는 가상 머신 실행 환경에서 실행되기 때문에, , NET에 적합한 IL(Intermediate Language)로 컴파일됩니다.
  • C#의 경우. NET이 없는 시스템에선 애플리케이션 실행이 불가능하겠죠.
  • C++의 경우 컴파일 시간이 C#에 비해 비교적 느립니다. 고수준 언어 -> 저수준 언어로 변환하는 속도보다 고수준 언어-> 중간 수준 언어로 변환하는 속도가 빠르기 때문이죠.

 

3. 값 타입(일반 변수)

  • C++의 값 타입은 정적으로 할당한 모든 형태의 데이터입니다. 
  • C#의 값 타입은 기본 타입과 구조체만을 의미합니다.

 

4. 참조 타입

  • C++의 참조 값은 동적으로 할당한 모든 형태의 데이터와 &기호를 붙여 선언한 참조형 변수입니다.
  • C#의 참조 값은 클래스로 만든 객체를 참조 타입으로 취급합니다.

 

5. 포인터 및 동적 할당

  • C++의 모든 데이터 타입은 포인터 변수로 선언할 수 있습니다. 더불어, 동적 할당 또한 가능합니다. 하지만, 메모리 관리를 직접 해야 합니다.
  • C#은 포인터 개념이 존재하지만, 잘 사용하지 않는 것으로 알고 있습니다. C#은 가비지 컬렉션이 자동으로 메모리를 관리해 주기 때문에, 불안전한 포인터 사용을 꺼려하는 것 같습니다. 

 

#5. 게임 프로그래밍과 C++

1. C++ 사용 이유

  1. 관성적으로, 개발 환경과 미들웨어(Unreal) 등이 모두 C++로 개발되어 있기 때문입니다.
  2. C++로 작성된 코드는 컴파일 과정에서 어셈블리어(저수준 언어)로 변환되어, 하드웨어 수준의 구성 요소와 메모리에 보다 직접적인 제어가 가능해집니다. 결과적으로, 하드웨어와 그래픽 프로세스를 직접 제어하는 것이 가능해집니다.
  3. 개발자가 직접 메모리 할당과 관리를 수행함으로써, 방대한 양의 데이터 리소스를 최적화하기 용이합니다.