C언어의 소스코드가 실행파일로 만들어지는 과정을 통해 컴퓨터가 소스코드를 이해하는 과정을 알아보자.
빌드란
사람이 읽기 쉬운 소스코드를 기계어 명령어로 변환하는 과정
그 기계어 명령들을 모아 기계에서 실행 가능한 실행파일로 만드는 과정
C의 빌드는 4단계
빌드 과정을 살펴볼 예시 소스코드
/* adder.h */
int add(const int a, const int b);
/* adder.c */
#include "adder.h"
int add(const int a, const int b){
return a + b;
}
/* main.c */
#include "adder.h"
int main(void){
const int res = add(1,2);
return 0;
}
INPUT
: 소스코드 (adder.c)
주석 제거
매크로를 복사, 붙여넣기
#include
파일들을 열어서 내용 복사, 붙여넣기
#
으로 시작하는 것들은 모두 전처리기 지시문
#include
#ifdef
#ifndef
#endif
#define
인클루드가드
순환 헤더 인클루드 : 헤더파일이 서로를 인클루드하여 끝없이 소스코드가 늘어나는문제
b.h에서 a.h를 다시 인클루드 하려고 할때 A_H가 이미 있기 때문에 순환 헤더 인클루드 문제를 막아준다.
/* a.h */
#ifndef A_H
#define A_H
#include "b.h"
/* 생략 */
#endif /* A_H */
/* b.h */
#ifndef B_H
#define B_H
#include "a.h"
/* 생략 */
#endif /* B_H */
/* c.c */
#include "a.h"
OUTPUT
: 확장된 소스코드 (트랜슬레이션 유닛)
INPUT
: 확장된 소스코드 (트랜슬레이션 유닛)
트랜슬레이션 유닛을 읽으면서 한줄씩 어셈블리어 코드로 만든다.
어셈블리어 코드
어셈블리 코드는 기계어와 거의 1:1대응 되지만 아직 텍스트 파일 형태
어셈블리 코드가 생성된 순간부터는 해당 플랫폼에서만 동작 가능하다. (이미 자료형 크기 등이 결정된 상태)
어셈블리 코드는 아직 정의되지 않은 변수나 함수를 사용가능하다.
정의가 없는 부분은 컴파일러가 그냥 구멍으로 비워놓는다 (링커가 나중에 구멍을 메워준다.)
헤더를 통한 선언만으로 컴파일이 가능한 이유
OUTPUT
: 어셈블리 코드
_add # @add
# %bb.0:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
...
/* 이하 생략 */
INPUT
: 어셈블리 코드
0
, 1
로만 이루어진 코드)OUTPUT
: 오브젝트 코드
00 00 00 04 00 00 00 00 FF FF 5F 61 30 01 .../* 이하 생략 */
INPUT
: 오브젝트 코드 (모든 오브젝트코드들)
OUTPUT
: 실행파일 (.exe, .out)
라이브러리
.dll
파일이 없다는 오류 : 동적 라이브러리가 없다는 오류위 과정을 모두 마치면 최종 파일이 생성된다.