GCC는 무엇인가(ft. 컴파일러란)
GCC란?
GCC는 GNU 컴파일러 모음(GNU Compiler Collection)으로, GNU 프로젝트의 일환으로 개발되어 널리 쓰이고 있는 컴파일러입니다. GCC는 유닉스 / 리눅스 계열 플랫폼의 사실상 표준 컴파일러이다.
자유 소프트웨어 중에 가장 잘 알려진 것들 중 하나인 GCC는 원래 C만을 지원했던 컴파일러로 이름도 “GNU C 컴파일러” 였다. 그러나 나중에 C++, 자바, 포트란, 에이다 등 여러 언어를 컴파일 할 수 있게 되면서 현재의 이름으로 바뀌게 되었다.
GNU 시스템의 공식 컴파일러이므로 GCC는 많은 컴파일러와 운영 체제를 만드는 데 사용되었다. 시스템 네이티브 컴파일러를 사용했을 때 비해서 GCC를 사용하면 같은 파서로 코드를 처리하므로 이식성을 향상시킬 수 있다. GCC는 상용 컴파일러에 비해서 느린 코드를 생성했지만 최근 많이 개선되었다.
공식적으로 지원하는 언어는 C(gcc), C++(g++), Objective-C(gobjc), Fortran(gfortran), Ada(gnat), Go(gccgo), D(gdc)이다.
그러면 컴파일러는 무엇인가?
컴파일러(Compiler)란?
컴파일러는 특정 프로그래밍 언어로 쓰여 있는 문서를 다른 프로그래밍 언어로 옮기는 언어 번역 프로그램을 말한다.
컴파일러는 고급 프로그래밍 언어를 실행 프로그램으로 만들기 위해 저급 프로그래밍 언어로 바꾸는 데 사용된다.
원래의 문서를 소스 코드 혹은 원시 코드라고 부르고, 출력된 문서를 목적 코드라고 부른다. 목적 코드는 주로 다른 프로그램이나 하드웨어가 처리하기에 용이한 형태로 출력되지만 사람이 읽을 수 있는 문서 파일이나 그림 파일 등으로 옮기는 경우도 있다.
원시 코드에서 목적 코드로 올기는 과정을 컴파일이라고 한다.
컴파일러는 소스 프로그램을 읽어서 즉시 결과를 출력하는 인터프리터와는 구분된다. 그러나 현대에 들어 많은 인터프리터가 JIT 컴파일 등의 기술로 실시간 컴파일을 수행하므로, 컴파일러와 인터프리터 사의 기술적 구분은 사라져 가는 추세이다.
등장 배경
초기 컴퓨터 프로그램들은 어셈블리어로 작성되었다. 그러나 서로 다른 CPU 아키텍처가 등장할 때마다 매번 똑같은 프로그램을 서로 다른 어셈블리어로 작성하는 비용이 커지면서, 고급 프로그래밍 언어의 필요성이 대두되었다.
지켜야 할 조건
컴파일러에서 꼭 지켜야 할 두 가지 조건이 있다.
- 컴파일러는 옮김의 과정에서 프로그램의 뜻을 보존하여야 한다.
- 컴파일러는 입력으로 들어온 프로그램을 어떤 면에서든지 개선해야 한다.
예를 들어, 소스 코드를 기계어로 옮긴다면 기계가 이해할 수 없었던 언어를 기계가 이해할 수 있게 개선한 것이 된다.
컴파일 과정
소스 코드를 실행 파일로 만들기 위해 네 가지 과정을 거친다.
- 전처리 과정
- 컴파일 과정
- 어셈블리 과정
- 링킹 과정
이 4가지 단계를 묶어서 컴파일 과정, 빌드 과정이라고 부르기도 하고 컴파일 과정과 링킹 과정을 따로 나눠서 부르기도 한다.
- 대표적인 작업
- 주석 제거
- 헤더 파일 삽입
#include 지시문을 만나면 해당하는 헤더 파일을 찾아 헤더 파일에 있는 모든 내용을 복사해서 소스 코드에 삽입한다. 즉, 헤더 파일은 컴파일에 사용되지 않고 소스 코드 파일 내에 전부 복사된다. 헤더 파일에 선언된 함수 원형은 후에 링킹 과정을 통해 실제로 함수가 정의되어 있는 오브젝트 파일(컴파일된 소스 코드 파일)과 결합한다. - 매크로 치환 및 적용
#define 지시문에 정의된 매크로를 저장하고 같은 문자열을 만나면 #define된 내용으로 치환한다. 간단하게 말해 매크로 이름을 찾아서 정의한 값으로 전부 바꿔준다.
컴파일(Compilation) 과정
컴파일(Compilation) 과정은 컴파일러(Compiler)를 통해 전처리된 소스 코드 파일(*.i)을 어셈블리어 파일(*.s)로 변환하는 과정이다.
이 과정에서 우리가 일반적으로 컴파일하면 생각하는 언어의 문법 검사가 이루어진다. 또한 Static한 영역(Data, BSS 영역)들의 메모리 할당을 수행한다.
어셈블리(Assembly) 과정
어셈블리어를 설명하기 앞서 어셈블리어 정의를 살펴보자. 기계어는 다른 말로 명령어라고 부르는데 명령어는 0101010과 같은 이진수로 이뤄진 숫자로 CPU 종류마다 고유한 내용을 가지고 있다.
어셈블리어는 이런 명령어를 사람이 이해할 수 있게 부호화한 것으로 CPU 명령어(기계어)와 1대1로 매칭된다.
어셈블러(Assembler)를 통해 어셈블리어 파일(*.s)을 오브젝트 파일(*.o)로 변환하는 과정이다.
(+) 오브젝트 파일(Object File)이란?
사람이 알아볼 수 없는 기계어로 작성된 코드를 오브젝트 코드라고 부른다. 그리고 오브젝트 코드로 구성된 파일을 오브젝트 파일이라고 하며, 오브젝트 파일은 특정한 파일 포맷을 가진다.
- Windows: PE(Portable Executable)
- Linux: ELF(Executable and Linkin Format)
링킹(Linking) 과정
링킹(Linking) 과정은 링커(Linker)를 통해 오브젝트 파일(*.o)들을 묶어 실행 파일로 만드는 과정이다.
이 과정에서 오브젝트 파일들과 프로그램에서 사용하는 라이브러리 파일들을 링크하여 하나의 실행 파일을 만든다. 이때 라이브러리를 링크하는 방법에 따라 정적 링킹(Static Linking)과 동적 링킹(Dynamic Linking)으로 나눌 수 있다.
- 🔗 ref :