컴파일러 

 

프로그래밍을 배우기 시작하면서 첫부분에 배우는 주제 중에 가장 이해하기 힘든 것은 무엇일까?

 

전세계에 55만 부 이상 판매한 C 기초플러스(Stephan Prata)의 서장에서는 C언어의 특징과 컴퓨터에 구조에 대하여 간략하게 설명하고 소스코드의 컴파일에 대하여 이야기한다.

 

C에 관한 많은 교재와 강의가 있지만 대부분 첫 시간에 컴파일러에 대한 이야기 부터 시작한다.

 

왜냐하면 컴퓨터 구조에 대한 부분은 대략적으로 설명하는 것으로 넘어갈 수 있지만 컴파일러에 대한 이해가 없이 코드를 작성하는 것은 캄캄한 밤을 걷는 기분이 들기 때문이다. 당장 예제를 실행하더라도 컴파일을 해야 프로그램을 실행할 수 있다. 물론 지금은 IDE(통합개발환경)이 상당히 좋아졌다.

 

비주얼 스튜디오 코드도 학습용은 무료이고 오픈소스 환경에서 개발된 비주얼 스튜디오 코드나 아톰같은 텍스트 에디터도 엄밀히 IDE가 아니라 텍스트 에디터지만 확장 기능을 사용해서 환경을 셋팅해두면 쾌적하게 프로그램을 개발 할 수 있다.

 

그래도 컴파일러를 이해하는 것과 아닌 것의 이해도와 학습 진도는 차이가 날 것이다.

 

그래서 컴파일에 대한 내용을 기록해두려고 한다. 인터프리터에 대해서는... 결국 프로그램을 실행시킨다는 결과에 있어서는 차이가 없지만 내부적인 과정은 큰 차이가 있다. 컴파일에 비교하는 내용은 알아두면 좋을 것이다.

 


 

프로그래밍 언어는 크게 컴파일과 인터프리터 두 가지 방식으로 분류한다.

 

먼저 컴파일러에 대하여 알아보자. UNIX 운영체제를 작성하기 위해 고안된 C언어를 기준으로 설명한다. 다른 고수준 언어보다 C언어가 하드웨어에 가까이 있기 때문에 좀 더 명확하다.

 

In computing, a compiler is a computer program that translates computer code written in one programming language (the source language) into another language (the target language).

 

위키피디아의 정의에 따르면 컴파일러는 하나의 프로그래밍 언어로 쓰여진 컴퓨터 코드를 다른 언어로 translate 번역하는 컴퓨터 프로그램이다.

 

C언어로 프로그램을 작성하려면 코드를 작성해야 한다. 그런데 이 코드가 보통 영어로 쓰여져 있다.

 

영어로 쓰여진 명령어들을 모아놓은 텍스트 파일을 소스코드 혹은 원시코드라고 한다. 이제 이 프로그램을 실행시켜야 하는데 영어로 쓰여져 있다. (아스키 코드가 나열된 텍스트 파일)

 

printf("Hello World\n"); 같은 C언어의 코드다.

 

이 코드를 컴퓨터가 알아서 실행을 해주면 좋겠지만 컴퓨터는 디지털 논리회로(CPU)로 되있기 때문에 영어를 알아들을 수 없다. 영어가 아니라 어떤 사람의 언어도 자동으로 알아듣지 않는다. 컴퓨터의 모든 자료형이 0과 1로 되어있다는 이야기를 들었을 것이다. 0과 1을 이진수라고 하는데 이진수가 instruction set (명령어 세트)에 따라 입력되면 CPU를 작동 시킬 수 있다.

 

리눅스에서 hello world 를 출력하기 위한 코드는 아래와 같다. 2진수는 너무 길기 때문에 16진수로 바꾼 코드다. 어떤 언어를 사용하던 궁극적으로 운영체제에서 실행되는 모든 코드는 머신코드로 번역된 후 CPU에게 전달되서 실행한다.

 

# 는 주석이다. 그 앞의 숫자들을 보면 무슨 의미인지 알 수 없다. CPU는 이 숫자들을 받아서 명령을 실핸한다.

 

기계어는 CPU가 해석을 잘 할 수 있지만 사람이 봤을 때는 이해하기 힘들다.

 

한편 C언어로 만든 소스코드는 인간은 이해할 수 있지만 기계는 이해할 수 없다.

 

컴파일러가 소스코드를 기계어로 번역하는 과정이 컴파일이라고 한다. 사용자가 프로그램을 작성하고 컴파일러 프로그램을 실행시키면 CPU가 해석할 수 있는 기계어를 만들어낸다. 이 때 만들어진 파일을 오브젝트 코드라 한다.

 

그런데 기계어만 가지고 바로 실행 시킬 수는 없다. 링크라는 과정을 한번 더 거쳐야 한다. 소스코드에는 아직 시동코드(execute) 가 없다. 그리고 컴파일된 소스코드가 C언어의 라이브러리 같이 다른 코드를 호출하기 때문에 오브젝트 코드는 링커를 통해서 완전한 실행 코드로 번역된다. 더블클릭하여 실행되는 exe 파일은 링크과정을 완료한 실행파일이다.

 

여기까지가 컴파일과 링크다. 프로그램을 실제로 실행하기 전에 만들기 때문에 Compile time (컴파일 시간)이라고 한다. 만일 소스코드에 문제가 있으면 컴파일러는 오류를 검출하여 다시 컴파일 하도록 한다. 컴파일 과정에 발생하는 에러는 컴파일 에러라고 한다.

 

그 다음에 프로그램을 직접 실행하니까 작동이 잘 되다가 갑자기 오류가 나서 종료되었다. 실행과정 중에 나는 오류는 Runtime Error (런타임 오류)라고 한다. 런타임 오류는 치명적이다. 왜냐하면 컴파일 오류는 아직 일반 사용자에게 배포하지 않은 상태라서 수정이 가능하지만 런타임 오류는 사용자가 프로그램을 실행시키는 도중에 발생하기 때문에 손을 쓸 방법이 없다.

 

 

그렇긴 하지만 프로그램을 짤 때는 런타임 오류가 날 가능성까지 염두하기 때문에 어느정도 대비는 가능하다. 크기가 정해진 스택 메모리에 정적 타입(static type) 데이터를 올려서 사용한다. 컴파일 시간에 미리 검사를 다 하기때문에 런타임의 타입불일치 등의 문제를 예방할 수 있다. 실행시간(Runtime)에 어떤 데이터를 사용할지 이미 다 정해져 있기 때문에 컴파일을 잘하면 오류 발생률이 낮다.

 

또 C언어 컴파일러 방식의 장점은 소스 코드를 기계어 코드로 번역하기 때문에 일단 실행속도가 빠르다. 프로그램이 실행되면 운영체제가 코드를 메모리에 적재한다음 앱에 제어권을 넘겨서 프로그램이 CPU에게 직접 명령을 전달한다. 메모리에 있는 코드를 꺼내서 명령 코드를 전달하면 CPU는 그대로 실행시킨다.

 

반면 인터프리터는 무엇인가?

 

인터프리터

 

컴파일러에 대해서만 이야기 하면 그냥 아~ 그렇구나 라고 넘어가 수 있다.

 

인터프리터와 비교하는게 각각의 특성을 잘 알 수 있다. 인터프리터 언어의 기준은 파이썬으로 놓는다.

 

컴파일러가 외국어로 된 문서를 번역하는 것이면 인터프리터는 실시간 통역과 비슷하다.

 

통역은 실시간으로 이루어져야 한다. 한 문장 단위로 실행을 시킨다. 인터프리터 상에서 짧은 문장들로 작성하는 소스코드를 스크립트라고 한다.

 

스크립트 언어 종류

 

스크립트 언어도 길게 쓰면 결과로 봤을 때 소스코드를 컴파일 하는 것과 별 차이가 없어 보이기도 한다. 어떤 프로그램이 스크립트냐 컴파일이냐 약간 애매한 부분이 있는데 파이썬의 쉘(Shell) 환경 처첨 프롬프트가 사용자의 입력을 기다리고 있는 환경에서 사용하면 스크립트라 부른다.

 

 

파이썬 쉘

인터프리터는 소스 코드를 한꺼번에 컴파일 하지 않는다. 한 줄씩 읽어서 실행시간(Runtime)에 실행시킨다. 언뜻 보면 이 방법이 더 나을 것 같은데 단점도 있다. 일단 실행시간이 느려진다. 소스코드는 그냥 UTF-8로 인코딩된 텍스트 파일일 뿐이다. 여전히 CPU가 알아들을 수 없다. 기계어로 변형시켜야 하는데 컴파일에서는 미리 끝나있던 것을 바로 해야하니까 컴파일 언어에 비해서 느리다.

 

또 컴파일 언어는 1메가 정도의 스택메모리에 정적 타입(Static type)의 데이터를 정리해놓고 빠르게 실행할 수 있는데 인터프리터는 힙메모리에 동적 타입을 사용하기 때문에 데이터 접근의 효율이 떨어진다. 같은 일을 하더라도 시간이 오래걸리고 작업 공간을 많이 사용해야 한다.

 

현대의 인터프리터 언어는 동적 타입(dynamic type)을 지원한다. 컴파일러 방식이 자료형에 엄격하다면 인터프리터는 프로그램이 실행될 때 자료형이 정해진다.

 

그래서 변수 앞에 자료형을 지정하지 않아도 된다. 변수에 할당하기 직전에 인터프리터가 판단한다. 정수, 문자열, 리스트, 클래스 등 그냥 변수 이름을 짓고 할당하면 된다. 자료형에 엄격한 컴파일러에서는 상상하기 힘들다.

 

 

변수 할당문을 실행하면 인터프린터는 자료형을 판별하고 크기에 맞는 공간을 계산하여 Heap 영역에 할당한다. 컴파일러에 비해서 처리해야할 명령어가 늘어나기 때문에 속도는 느려진다.

 

인터프리터와 컴파일러는 다르다. 속도만을 놓고 비교하면 인터프리터가 불리한데 각자 장단점이 다르고 사용분야에 차이가 있다.

 

여기있는 내용은 전부가 아님을 알고 있다. 훨씬 더 복잡한 부분들까지 포스팅 하나에 담기는 어렵다.

 

여력이 되면 차후에 내용을 보완하기 위해 쓴 초안이다.

 

* 프로그램을 처음 배우는 사람에게 이 모든 혼란스러운 내용을 전달할 필요는 없다. 하지만 좀 더 쉽게 알아들을 수 있는 방법을 찾는 노력은 해야할 것 같다.

공유하기

facebook twitter kakaoTalk kakaostory naver band