지금 내가 듣고 있는 자바스크립트와 타입스크립트이지만 타입스크립트 강의를 시작하면서 강사님이 C언어에 대해 한 주동안 설명해주셨다. 기왕 C언어를 공부하는 김에 C언어의 컴파일 과정에 대해서 공부해보면 좋을 것 같아 정리를 해보았다. 사실 컴파일을 하는 과정에서 상당히 많은 에러가 발생하고 이 컴파일 과정을 알고 있다면 에러를 이해하는 데에도 도움이 된다.
뭐 물론 자바스크립트만 판다면 사실 필요하지 않을 수도 있지만... 그래도 사람 일은 모르기 때문에... 지식을 쌓아둬서 나쁠 것도 없고 말이다.
일단 컴파일이란 간단하게 말하자면 컴퓨터, 즉 기계가 읽기 쉽게 기계어로 변환해주는 과정이라고 보면 된다. 아래에서도 예시 사진을 보여주겠지만 기계어는 사람이 읽을 수가 없고 그렇기에 사람들은 고수준 언어, 사람에 가까운 언어로 먼저 코드를 작성한 후 기계어로 변환하게 된다.
그 중에서도 C언어는 저수준 언어에 속하기 때문에 개발자가 메모리에도 직접 접근하는 등 오류 발생도 빈번하기도 하고 코드 작성도 비교적 복잡하다. 대신 메모리를 직접 접근하는 만큼 효율적으로 사용할 수도 있고, 일단 실행 속도가 빠르다. 보통 이런 언어는 임베디드 시스템 쪽에서 자주 사용한다.
우선 우리가 작성한 c 파일의 컴파일 과정을 간단하게 나타내면 위 사진과 같다. 전처리 과정, 컴파일 과정, 어셈블 과정, 링킹 과정으로 나누어진다.
부연 설명으로 적혀있듯이, 우선 전처리 과정에서는 헤더 파일 삽입
, 매크로 치환
과정이 이루어진다. 그 후 컴파일 과정에서는 코드를 어셈블리어로 변환해준다. 그리고 어셈블 과정에서 비로소 기계어로 변환이 된다. 이제 소스 파일과 라이브러리를 결합해서 최종적으로 실행 파일을 생성하게 된다.
이제 각 과정을 예시와 함께 공부해보자.
우선 컴파일 과정을 확인하기 위해 위와 같은 간단한 덧셈 코드를 만들었다. 매크로가 치환되는 전처리 과정을 보이기 위해 A와 B를 매크로로 정의했다.
gcc
를 통해서 어떻게 컴파일이 되는지 알아보자. 중간 과정을 모두 건너 뛰고 컴파일을 하기 위해서는 아래와 같이 작성하면 된다.
gcc calculator.c -o calculator
program.exe
파일이 생성될 텐데 ./program
명령어를 이용하여 실행하면 된다.
다만 나는 컴파일 과정을 이해하기 위해 각 단계까지만 수행하였다. 먼저 전처리 과정만 수행하기 위해서 아래와 같이 입력해주자.
gcc -E calculator.c -o calculator.i
생성된 calculator.i
파일을 한 번 열어 보자.
뭐가 엄청나게 많다. #include
로 포함한 헤더 파일을 받아왔다고 보면 된다. 중간에 보면 우리가 포함한 헤더 파일인 stdio.h
가 보일 거다.
밑으로 쭉 내려보면 우리가 입력했던 #define
매크로는 사라지고 매크로로 정의한 숫자들이 a와 b에 잘 들어가 있는 걸 확인할 수 있다!
즉! 전처리 과정에서는 #include
와 #define
과 같은 전처리 지시자를 해석한다. 주로 stdio.h
나 stdlib.h
같은 헤더 파일을 받아오고 매크로를 치환하는 작업이 이루어진다고 보면 될 것 같다.
컴파일 과정까지만 수행하기 위해서는 아래와 같이 입력해주면 된다.
gcc -S calculator.i -o calculator.s
ls 명령어로 확인해보면 calculator.s
파일이 잘 생성된 걸 볼 수 있다. 이제 이 파일을 한 번 열어보자.
짠.
좀 더 이상해졌다. 굳이 하나하나 뜯어볼 필요는 없고 그냥 어셈블리어로 변환이 됐다... 이 정도만 알면 될 것 같다. 이 컴파일 과정에서 전단부, 중단부, 후단부로 나뉘는데 이거까지 설명한다면 부가적으로 설명할 게 너무 많아서 이건 차후에 따로 다뤄보는 걸로... (사실 아직 나도 불특정다수에게 설명할 만큼 잘 알고 있는 게 아님)
이제 최종적으로 컴퓨터가 읽을 수 있게 기계어로 변환하는 과정이다.
gcc -c calculator.s -o calculator.o
어셈블 과정만 진행하기 위해서는 위처럼 입력해주면 된다. 정상적으로 object 파일인 calculator.o
파일이 생성된 걸 볼 수 있다. 이제 저 파일을 열어보면...
그냥 닫자.
아무튼 우리가 읽을 수 없는 그런 컴퓨터만 읽을 수 있는 기계어로 최종적으로 변환이 된 걸 확인할 수 있다.
자 이제 최종 링킹 과정이다. 생성된 목적 파일 (*.o) 파일과 표준 C 라이브러리, 사용자 라이브러리를 링크해주고 비로소 우리가 실행할 수 있는 실행 가능한 파일이 만들어지게 된다.
gcc calculator.o -o calculator.exe
이 링킹 과정을 진행하기 위해서 위와 같이 입력해주자. .exe는 붙여도 되고 안 붙여도 된다. 정상적으로 실행 파일까지 생성이 되었다.
./calculator.exe
로 해당 파일을 실행을 해주면 아주 정상적으로 컴파일 후 실행이 되는 걸 확인할 수 있다.
보통은 gcc 소스 파일 -o 실행 파일명
을 입력해서 실행 파일을 바로 만들지만 실제로는 위와 같은 과정들이 이루어졌다는 걸 알 수 있다.
이렇게 C언어에서 컴파일 과정이 어떻게 일어나는지 알아보았다. 이전에 교수님께서 컴파일 과정을 알고 있는 개발자와 모르는 개발자의 차이는 크다고 강조하셨다. 솔직하게 말해서 큰 체감을 느끼지는 못했는데 그래도 프로그램을 이해하는 데에는 도움이 되지 않았나... 싶다. 마침 컴파일러 수업을 듣고 그 다음 들었던 수업이 임베디드 수업이어서 좀 더 시너지가 났던 것 같기도 하고🤔