수학기술지원 [969976] · MS 2020 · 쪽지

2021-02-17 22:30:46
조회수 951

매우 쉬운 C언어 알아보기 2: 소스 파일의 여정

게시글 주소: https://orbi.kr/00036298469

방금전 작성한 소스 파일

방금 전에 아무 뜻도 모르고 작성한 소스 파일이 있었습니다. hello_world.c 파일이었습니다.

C언어 소스 파일이란, 우리가 C언어로 작성하여 컴퓨터에게 이러한 작업을 처리해 달라는 설계도로 생각하시면 됩니다.

하지만, 컴퓨터는 매우 멍청하기 때문에, 0과 1밖에 모릅니다. 이러한 바보멍청이에게 C언어를 가져다 주어 봤자 이해하지 못합니다. 그러기 위해서는, 컴파일러라는 프로그램이 필요합니다.


컴파일러

컴파일러란, 어떤 언어를 다른 언어로 바꾸어 주는 프로그램을 말합니다. 현재 우리가 사용하는 GCC 컴파일러는, 

C언어를 컴퓨터가 이해할 수 있는 기계어로 바꾸어 줄 겁니다. 이러한 과정을 컴파일이라고 합니다. (Compile)


컴파일 과정 살펴보기

컴파일 과정을 살펴보는 것은 매우 중요합니다.

먼저 우리가 작성한 소스 파일 foo.c를 생각해 봅시다.


Image result for compile process


소스 코드는 먼저 전처리기(Preprocessor)라는 친구에게 전달됩니다. 이 친구는 헤더 파일에 있는 소스를 우리의 소스 파일 foo.c에 붙여 넣어 주고 (정말 텍스트를 복사-붙여넣기를 하는 것으로 생각하면 됩니다.) , 프로그래머가 코드를 쉽게 볼 수 있도록 써놓은 주석은 제거해 둡니다. 


헤더 파일


헤더는 도대체 무엇일까요? 방금 전에 작성한 코드를 살펴 봅시다.

#include <stdio.h> 에서, <> 안에 둘러쌓여 있는 .h로 끝나는 것이 헤더 파일입니다.

헤더 파일이란, 함수의 정보와 변수에 대한 정보를 담고 있는 안내서라 생각하면 됩니다. stdio.h 헤더 파일은 어떤 함수에 대한 정보와 변수에 대한 안내가 들어 있는 것일까요?

stdio란 Standard input output의 줄임말입니다. 표준 입출력이라는 뜻인데요, 우리가 작성했던 printf, 출력을 담당하는 함수에 대한 정보를 담고 있는 안내서입니다. 

어떤 함수를 쓰려고 하면, 그 함수에 대한 정보가 필요하겠죠. 이러한 함수를 쓰기 전에, 그 함수에 대한 정보를 불러 들이겠다, 라는 의미로 #include 키워드를 사용했다고 생각하시면 됩니다.


다시 돌아와서


foo.c는 전처리기에 의해 foo.i라는 파일로 변환됩니다. 이러한 전처리된 파일은 컴파일러에게 가져다 줍니다.


컴파일러는 무슨 일을 할까


이제 이 전처리된 파일을 컴파일러가 .s 로 끝나는 어셈블리 언어로 바꾸게 됩니다. 우리가 작성한 foo.c를 컴파일러는 foo.s로 다음과 같이 바꿉니다. 

어셈블리어로 변환된 파일을 보고 싶다면, 우분투 운영체제 쉘에서 다음 명령어를 입력하면 됩니다.


1
gcc -S foo.c
cs




변환된 파일은 다음과 같습니다. 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 110    sdk_version 111
    .globl    _main                   ## -- Begin function main
    .p2align    40x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq    _printf
    xorl    %ecx, %ecx
    movl    %eax, -8(%rbp)          ## 4-byte Spill
    movl    %ecx, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz    "Hello, World!"
cs




제 컴퓨터는 맥북이기 때문에, 인텔 CPU위에서 동작합니다. 우리의 컴파일러가 제 컴퓨터에 알맞게 동작하도록 어셈블리 언어를 변환한 결과입니다. 다음 명령어는 인텔 CPU에서 동작하는 어셈블리 언어로 컴파일러가 변환해 준 것입니다. 

위 내용은 전혀 알 필요가 없습니다. 나중에 컴퓨터구조나, 시스템 프로그래밍을 공부하면 직접 어셈블리 언어로 프로그래밍 하는 방법을 배울 수 있을 것입니다. 컴파일러는 이 어셈블리 언어로 변환된 foo.s를 어셈블러에게 전달합니다.


어셈블러


어셈블러는 어셈블리 언어를 .O, 오브젝트 코드로 변환합니다. 이 단계가 데이터와 기계어를 포함하는 파일이죠. 하지만, 이 과정에서 다 끝난 것이 아닙니다.


링커


링커는 번역된 오브젝트 코드를 하나로 모아 실행 파일을 만듭니다. 나중에 아주 큰 프로젝트를 진행하게 되면, 소스 파일은 대단히 많아지고, 헤더 파일도 대단히 많아질 것입니다. 이러한 거대한 하나하나의 소스 파일이 오브젝트 코드가 되는데요, 이러한 여러 개의 목적 코드를 하나로 합쳐주는 프로그램이 링커입니다. 긴 여정이 끝나고, 우리가 실행 파일을 실행하면 명령 창(쉘)에 


Hello,World!


가 출력되는 것입니다.


0 XDK (+0)

  1. 유익한 글을 읽었다면 작성자에게 XDK를 선물하세요.