ㅇ공부#임베디드/시스템 프로그래밍

2. C언어의 컴파일 과정 이해하기

BrainKimDu 2023. 9. 2. 21:52

이 글은 다음의 강의를 정리 및 복습하는 글임을 밝힙니다. 또한 면저 준비시에 활용합니다.

http://kocw.or.kr/home/cview.do?mty=p&kemId=1223639&ar=relateCourse 

 

시스템 프로그래밍 기초

이 교과목은 컴퓨터 하드웨어 시스템과 운영체제 기반에서 프로그래밍 언어를 어떻게 사용하는지에 관한 기초에 대해서 학습한다.

www.kocw.net

 


C언어의 개발과정을 이해해보자.

주요한 키워드는 다음과 같다.
- Edit
- Preprocess 
- Complie
- Link
- Load
- Execute

1. Edit
코드를 직접 수정하는 단계이다. 예를 들자면 VScode로 코드를 수정하는 과정이다.

2. Preprocess
선처리라고 하며, 코드를 분리하고 컴파일러에게 정보를 제공한다고 한다. 주로 코드의 가독성을 높이는데 초점이 맞추어져 있습니다.
- 헤더파일 : 코드내에 존재하는 헤더파일을 현재 파일로 가져옵니다.
- 매크로 처리 : 매크로를 처리합니다. 
- 주석 제거 : 주석을 체거합니다.
- 조건부 컴파일 : #ifdef #ifndef #else #endif 와 같은 지시문을 사용하여 득정 조건에 따라 코드의 일부를 컴파일하거나 제외

3. Complie
주어진 코드를 분석하여 문법적오류를 확인한 이후 어셈블리어로 변환합니다. 

4. Linker
여러 개의 코드를 하나의 실행 파일로 결합하고, 필요한 외부 라이브러리와 연결합니다. 결고적으로 실행파일이 만들어집니다.

5. Loader
이제 실행파일을 메모리에 올립니다. 그리고 CPU에 의해 실행됩니다. 

 

리눅스로 한 번 확인해보자.

리눅스에서 C/C++의 컴파일을 위해 사용하는 것은 Gcc와 G++입니다. gcc는 다음의 기능을 제공합니다.
- Preprocessing
- compilation
- assembly
- linking

그러면 c파일을 하나 만들고 진행하도록 하겠습니다.

이제 Vi editor를 통해서 코드를 작성해봅시다.

어오 힘들어

이뻐보이게 할려고 뒤에 줄바꿈 문자를 추가합니다.

이제 gcc를 통해서 빌드를 해보겠습니다.

다음처럼 a.out파일이 생성된 것을 볼 수 있습니다.

다음처럼 실행하면 Hello world가 출력되는 것을 확인할 수 있습니다.

 

빌드에는 다양한 방법이 있습니다.

-c 를 붙이는 경우

test.o가 생성되는 것을 확인할 수 있는데, 이게 목적코드 혹은 목적파일입니다. -c를 넣는 것은 linking은 하지 마세요라는 뜻입니다.  목적코드는 어떻게 생겼는지 확인해봅시다.

text editor로는 열 수 없다고 합니다. 아쉽지만 계속 나아가 봅시다.

 

-o를 붙이는 경우

원하는 실행파일의 이름을 지정할 수 있습니다.

gcc test.c -o hello

 

-g를 붙이는 경우

object code에 대한 디버깅 정보를 출력한다고 합니다. 이에 대한 정보는 chatgpt에 추가로 물어보겠습니다.

gcc -g test.c -o hello

chatgpt말로는 gdb를 통해서 디버깅이 가능하다고 합니다. 이에 대해서 더 깊은 이야기가 있는 것 같습니다만 우선 넘어가도록합시다.

 

 

이 외에

-Wall
모든 경고를 알려준다.
-i <path>
include해야할 파일의 위치를 알려준다거나
-L<path>
non default shard libraries를 찾게 해준다고 합니다.
-l <library name>
library를 include하게 한다거나
-Werror
에러를 찾을 수 있다고 합니다.

 

 

Makefile

앞서 본 것처럼 하나하나 gcc를 통해서 코드를 실행할수는 없습니다. 여러개의 코드를 한 번에 실행할 방법이 없을까? 라는 생각에 나온 것이 Make입니다.

터미널에서 make를 입력하면 다음과 같은 결과가 나오게됩니다.

우리가 make를 입력하면 터미널에서는 먼저 Make.txt 파일을 찾습니다. 그리고 이를 실행시킵니다. 

추가적으로 CMake를 이용하여 OpenSource를 설치한 기억이 다들 있을 것입니다. 여기서 CMake는 모두의 환경이 다르기 때문에 각자의 환경에 맞게 Make파일을 생성해주는 친구라고 합니다.

 

3개의 파일을 만들어봅시다. A, B, C입니다.

#include <stdio.h>

void b_function();
void c_function();

int main()
{
    printf("A\n");
    b_function(); 
    c_function();
    return 0;
}
#include <stdio.h>

void b_function()
{
    printf("B\n");
}
#include <stdio.h>

void c_function()
{
    printf("C\n");
}

이렇게 만들었습니다. 일단 컴파일은 하지 않고, make파일도 작성해봅시다.

이제 make파일을 만들어봅시다. 파일이름은 Makefile로 해주어야 합니다. 

hello: A.o B.o C.o
	gcc A.o B.o C.o -o hello
	
clean:
	rm -rf *.o hello

A.o: A.c
	gcc -c A.c
	
B.o: B.c
	gcc -c B.c
	
C.o: C.c
	gcc -c C.c

우선 한줄한줄 이해해봅시다. 

make파일은 두 개의 줄로 이루어져있습니다. 위는 rule 아래는 shell이라고 합니다. 

맨위부터 일단 실행이 됩니다. rule에서 오른쪽에 적힌 A.o B.o C.o는 dependency부분입니다. shell을 확인해보면 A.o B.o C.o를 hello라는 실행파일로 만들어라 입니다. shell에서는 tap을 꼭 써주어야합니다. 

dependency에 의해 내려가서 확인을 해보니 A.c를 링킹을 제외하고 컴파일하라고 합니다. B.c  C.c 또한 마찬가지입니다.

clean은 목적파일과 실행파일을 삭제하는 것을 의미하고, 컴파일전의 깨끗한 상태로 돌리는 것을 의미합니다.

그래서 결론적으로 make hello 나 make clean 을 터미널에 입력하여 실행할 수 있습니다.

그래서 실행하면 이렇게 됩니다.

삭제도 가능합니다.

 

Makefile 조금더 깊게 들어가보기

이제 Make파일을 조금 수정해봅시다.

#을 통해서 주석을 달 수 있습니다.

 

메크로를 통해서 접근이 가능합니다. 만약에 gcc를 바꿔주고 싶은 상황이 올 수 있습니다.

그러면 여기에서 gcc부분을 하나하나 수정해주어야합니다. 이를 매크로로 지정하면 편리하게 사용이 가능한데

이렇게 사용할 수 있으며 gcc를 뭐 C++을 쓴다면 

다음처럼 변경하면 끝입니다. 그래서 메크로가 편리하다고 합니다. 뭐 이런식으로 매크로를 넣어줄 수 있습니다.