make나 clang을 사용해서 프로그램을 실행할 때 아래 네 개의 단계를 거친다.

전처리(Precompile)

# 으로 시작되는 C 소스 코드는 전처리기에게 실질적인 컴파일이 이루어지기 전에 무언가를 실행하라고 알려준다.

예를 들어, #include는 전처리기에게 다른 파일의 내용을 포함시키라고 알려주고 프로그램의 소스 코드에 #include 와 같은 줄을 포함하면, 전처리기는 새로운 파일을 생성하는데 이 파일은 여전히 C 소스 코드 형태이며 stdio.h 파일의 내용이 #include 부분에 포함된다.

string get_string(string prompt); // #include <cs50.h>
int printf(string format); // #include <stdio.h>

int main(void)
{
	string name = get_string("What's your name?\\n");
	printf("hello, %s\\n", name);
}

컴파일(Compile)

컴파일러라고 불리는 프로그램은 C 코드를 어셈블리어라는 저수준 프로그래밍 언어로 컴파일 한다.

C 코드를 어셈블리 코드로 변환시켜줌으로써 컴파일러는 컴퓨터가 이해할 수 있는 언어와 최대한 가까운 프로그램으로 만들어 주고 전처리한 소스 코드를 어셈블리 코드로 변환시키는 단계를 말하기도 한다.

main:
	.cfi_startproc
# BB#0:
	pushq. %rbp
.Ltmp0:
	.cfi_def_cfa_offset 16
.Ltmp1:
	.cfi_offset %rbp, -16
	mova %rsp, %rbp
.Ltmp2:
	.cfi_def_cfa_register %rbp
	suba $16, %rsp
	xorl %eax, %eax
	movl %eax, %edi
	movabsq $. L.str, %rsi
	movb $0, %al
	calla get_string
	movabsq $.L.str.1, %rdi
	mova %rax, -8(%rbp)
	mova -8(%rbp), %rsi
	movb $0, %al
	calla printf

어셈블(Assemble)

소스 코드가 어셈블리 코드로 변환되면, 다음 단계인 어셈블 단계로 어셈블리 코드를 오브젝트 코드로 변환(연속된 0과 1들로 바꿔주는 작업) 한다.

이 변환작업은 어셈블러라는 프로그램이 수행하고 소스 코드에서 오브젝트 코드로 컴파일 되어야 할 파일이 딱 한 개라면, 컴파일 작업은 여기서 종료된다. 그러나 그렇지 않은 경우에는 링크라 불리는 단계가 추가로 진행한다.