[C언어] #1 프로그램의 빌드과정

Ilhoon·2022년 1월 20일
1
post-thumbnail

C프로그램의 빌드과정

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)

  1. 주석 제거

  2. 매크로를 복사, 붙여넣기

  3. #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. 어셈블리 코드는 기계어와 거의 1:1대응 되지만 아직 텍스트 파일 형태

      2. 어셈블리 코드가 생성된 순간부터는 해당 플랫폼에서만 동작 가능하다. (이미 자료형 크기 등이 결정된 상태)

      3. 어셈블리 코드는 아직 정의되지 않은 변수나 함수를 사용가능하다.

        • 정의가 없는 부분은 컴파일러가 그냥 구멍으로 비워놓는다 (링커가 나중에 구멍을 메워준다.)

        • 헤더를 통한 선언만으로 컴파일이 가능한 이유

OUTPUT : 어셈블리 코드

_add						# @add
# %bb.0:
			pushl	%ebp
			movl	%esp, %ebp
			subl	$8, %esp
			...
/* 이하 생략 */

어셈블러

INPUT : 어셈블리 코드

  1. 어셈블리 코드를 읽으면서 오브젝트 코드로 만든다.
    • 오브젝트 코드
      1. 기계가 곧바로 이해가능한 기계코드 (기계어)
      2. 이진 코드 (0, 1 로만 이루어진 코드)

OUTPUT : 오브젝트 코드

00 00 00 04 00 00 00 00 FF FF 5F 61 30 01 .../* 이하 생략 */

링커

INPUT : 오브젝트 코드 (모든 오브젝트코드들)

  1. 모든 오브젝트 코드를 모아 구멍을 메꾼다
  2. 오브젝트 코드에 레이블을 보고 함수의 주소를 기억하고 있다가
  3. 해당 함수를 호출하는 코드를 보면 기억해놨던 주소로 점프하는 코드를 넣어준다.

OUTPUT : 실행파일 (.exe, .out)

라이브러리

  • 최종 파일이 실행 파일이 아닌 라이브러리 파일이 나오게끔 빌드할 수도 있다.
  • 오브젝트파일을 모아서 하나로 만들어 놓고 필요할 때 가져다 쓸 수 있도록 하는 것. (사용할 때 같이 링크해서 사용)
  • 정적 라이브러리
    • 라이브러리 안에 있는 기계어를 최종 실행파일에 가져다 복사함
    • 실행파일 크기가 커지고 메모리 사용 증가
    • 실행 속도가 빠름
  • 동적 라이브러리
    • 실행파일안에 여전히 구멍을 남겨두는 방법
    • 실행파일을 실행할 때 링킹이 일어난다 (운영체제가 링킹)
    • 실행파일 크기가 작다.
    • 여러 실행파일이 동일한 라이브러리를 공유할 경우 메모리 절약 장점 (정적 라이브러리는 실행파일마다 복사되서 낭비)
    • .dll파일이 없다는 오류 : 동적 라이브러리가 없다는 오류

위 과정을 모두 마치면 최종 파일이 생성된다.

profile
꾸준하게!

0개의 댓글