프롤로그
ELITE HACKER Bootcamp 3rd 2주차 수업 공부 내용
aws 우분투 서버 하나 파서 연습
C언어, 컴파일러, gcc
C언어란?
UNIX 운영체제 개발을 위해 설계한 범용 프로그래밍 언어
하드웨어와 밀접하게 작업할 수 있는 저수준 프로그래밍 부터 고수준 프로그래밍 까지 갖추고 있다.
- 절차적 프로그래밍 언어: C는 프로그램이 순차적으로 실행되는 절차적 구조이다.
- 메모리 접근 제어: C 언어는 포인터를 통해 메모리 주소로 직접 참고 -> 하드웨어에 용이
- 컴파일러 기반 언어: C 프로그램은 소스 코드를 컴파일하여 실행 파일로 변환한 후 실행. gcc 사용
- 포터블(이식성): 대부분의 운영체제에서 C 컴파일러를 사용하면 동일한 C 코드가 쉽게 이식되어 다른 플랫폼에서 실행 가능
컴파일러 vs 인터프립터
컴파일러 언어, 인터프립터 언어에 대해서 알아보자.
컴파일러(Complier)
우리가 작성한 소스코드를 모두 기계어로 번역해주는 과정이다. 기계어로는 어셈블리어가 있는데, 어셈블리어는 기계어와 일대일 대응이 되는 컴퓨터 프로그래밍의 저급 언어이다. 컴파일러는 초기 스캔을 마치면 실행파일을 만들어 놓고 다음에 실행할때 이전에 만들어 놓았던 실행파일을 실행한다. 즉, 코드 전체를 하나의 실행 파일로 만들어서 컴퓨터에게 전달하는 것. 그렇기 때문에 매우 빠르다. C언어, 자바등이 컴파일러 언어다. 미리 소스코드를 전부 해석해서 기계어로 바꿔주기 때문에, 소스코드에서 오류가 발생하면 컴파일이 되질 않는다. 즉 오류를 미리 발견해서 수정을 할 수가 있다라는 의미이다.
인터프리터(interpreter)
한 줄 실행할 때마다 한 줄씩 번역한다. 그래서 실질적으로 굉장히 느리고, 오류 발견이 좀 늦을 수 있다. 예를 들어서 파이썬에서
combine = 3
comblne = 5
print(combine)
이렇게 변수에 오류가 났을 때 찾아내기가 힘들다. 그럼 인터프리터 개쓰레기라서 쓸 이유가 없지않나? 하는데
인터프리터는 사람친화적이다. 컴파일러와 같은 오브젝트 코드 생성과정이 없기 때문에 메모리 효율이 좋고, 한 줄에 한 번씩 실행하기에, 어디서 오류나는 지 어느과정에서 오류나는 지 바로 알 수가 있다.
대표적으로 파이썬이 인터프리터 언어이다.
컴파일 과정과 gcc
컴파일을 도와주는 컴파일러 도구가 있는데, 그게 바로 gcc이다. 컴파일은 일종의 실행파일을 만드느 거라고 그냥 생각을 해놓자. 지금부터 그 과정들을 gcc를 이용해서 확인해볼 것이다.
서버에 접속해서,
nano hello.c
한 다음에
#include <stdio.h>
int main(){
printf("Hello World");
return 0;
}
의 간단한 c언어 파일을 만들고 테스트 해보자.
gcc를 사용하기 위해서는 우선, gcc를 설치해야한다.
sudo apt install gcc
gcc 사용법을 미리 알아보자면
gcc [속성] [파일명]
이렇게 사용하는데,
gcc hello.c
하면 a.out이 나온다.
./a.out
a.out을 실행하면, 내가 작성한 저 코드가 실행이 되는 것이다.
a.out이라고 gcc를 하면 강제적으로 나오니까, 이름을 지정해줘서 실행파일을 만들어 줄려면 -o 속성을 이용한다.
출력 파일의 이름을 지정할 때 사용한다.
gcc -o hello hello.c
하면 hello라는 실행파일이 hello.c가 컴파일되어서 나온다.
이렇게 gcc 의 기본적인 사용법과 -o 속성을 알았으니, 본격적으로 어떻게 컴파일되는지 확인해보자.
(1) 전처리 과정
전처리 단계는 소스 코드에서 전처리 지시자(#include, #define 등)를 처리하는 단계이다. 우리가 짠 소스코드에서도
#include <stdio.h>
이게 있는데, 이것을 처리하는 거라고 보면 된다. 그렇다면 #include <stdio.h>가 무엇을 의미하느냐?
전처리 지시자, 즉 헤더파일을 불러오는 것이다. 해당 헤더 파일의 내용을 소스 코드에 그대로 복사하여 포함시킨다. stdio.h에는출력을 하기 위한 printf나 입력을 받기 위한 scanf가 존재한다. 저 함수들을 처리하는 헤더파일들을 처리하는 과정이라고 보면 된다. 또는
#define등 매크로 정의를 위해 사용되는 것을 처리하기 위한 작업이기도 하다.
gcc 에서 전처리만 수행하기 위해서는 -E 속성을 사용한다.
gcc -E hello.c -o hello.i
전처리된 파일은 hello.i , 즉 .i파일로 저장된다.
사람이 읽을 수 있다.
(2) 컴파일 과정
컴파일 과정은 전처리된 소스 코드를 어셈블리어로 변환하는 단계이다. 이 단계에서 소스 코드는 기계어에 가까운 코드로 변환되며, 컴파일러는 코드의 구문과 의미를 분석하고 최적화를 수행한다. 이때 코드가 최적화 되고, 어셈블리어 코드가 생성된다.
gcc -S hello.i -o hello.s
어셈블리어로 전환한 코드는 -S로 확인 가능하다.
코드가 전환이 되었다. 여기까지도 사람이 있을 수 있다.
(3) 어셈블리 과정
어셈블리(Assembly)는 컴파일된 어셈블리어 코드를 **기계어(Machine Code)**로 변환하여 **오브젝트 파일(Object File)**을 생성하는 단계이다.
즉 목적파일을 만든다. 이 목적파일은 기계어 코드이다. -c를 이용하여 만들 수 있다.
gcc -c hello.s -o hello.o
여기서부터는 기계어기 때문에 사람이 알아볼 수 없는 것들이 조금씩 생겨나고 있다.
근데
gcc: warning: hello.o: linker input file unused because linking not done
이러한 문구가 뜬다. 링킹이 아직 되지 않아서 당신은 이 목적파일을 사용할 수 없다. 라고 뜬다.
(4) 링킹 과정
위에서 만들어진 파일은 기계어 코드를 포함하는 바이너리 파일이다. 다른 오브젝트 파일이나 라이브러리가 링킹되어야만 실행될 수 있다.
링킹(Linking)은 여러 개의 오브젝트 파일(Object Files) 및 라이브러리를 결합하여 실행 가능한 프로그램을 생성하는 과정이다. 링킹은 프로그램이 컴파일된 후 최종 실행 파일이 생성되기 전에 수행되는 중요한 단계이다.
막 동적링킹, 정적 링킹이 있는데, 넘어가고...
이제 이걸
gcc hello.o -o hello
링킹해서 실행파이를 만들면 끝.
file *
를 통해서, 파일들이 어떻게 생겨져 있는 지 볼 수 있다.
에필로그
이를 토대로, 이제 리눅스 환경에다가 C언어 파일을 작성하고, 실행파일로 만드는 과정을 진행하였다
다음 글 부터 C언어의 자료형부터 오버플로우 까지 한 번 알아보자.
'KnockOn' 카테고리의 다른 글
[KnockOn] Linux/Ubuntu C언어 연산자 (0) | 2024.11.13 |
---|---|
[KnockOn] Linux/Ubuntu C언어 자료형 (0) | 2024.11.12 |
[KnockOn] Linux/Ubuntu SSH, NC (0) | 2024.11.07 |
[KnockOn] Linux/Ubuntu 방화벽, ufw (1) | 2024.11.07 |
[KnockOn] Linux/Ubuntu 리눅스 권한, 유저, 그룹 (3) | 2024.11.07 |