CS/cs50

[cs50] 컴파일, 디버깅

juyeong 2022. 4. 9. 09:26
반응형

컴파일 과정(Compile)

컴파일 소스 코드를 오브젝트 코드로 변환시키는 과정입니다. 여기서 소스 코드는 여러분이 C언어와 같은 프로그래밍 언어로 작성한 코드이고, 오브젝트 코드 기계 코드라고도 알려져 있는데, 0과 1로 이루어져 있으며 컴퓨터에게 프로그램이 어떻게 실행되어야 하는지 알려주는 코드입니다. make 명령어 자체는 컴파일러가 아니고, clang이라는 컴파일러를 호출해서 C 소스 코드를 오브젝트 코드로 컴파일 하도록 합니다.

전처리(Precompile)

컴파일의 전체 과정은 네 단계로 나누어볼 수 있습니다. 그 중 첫 번째 단계는 전처리인데, 전처리기에 의해 수행됩니다. # 으로 시작되는 C 소스 코드는 전처리기에게 실질적인 컴파일이 이루어지기 전에 무언가를 실행하라고 알려줍니다.

예를 들어, #include는 전처리기에게 다른 파일의 내용을 포함시키라고 알려줍니다. 프로그램의 소스 코드에 #include 와 같은 줄을 포함하면, 전처리기는 새로운 파일을 생성하는데 이 파일은 여전히 C 소스 코드 형태이며 stdio.h 파일의 내용이 #include 부분에 포함됩니다.

 

컴파일(Compile)

전처리기가 전처리한 소스 코드를 생성하고 나면 그 다음 단계는 컴파일입니다. 컴파일러라고 불리는 프로그램은 C 코드를 어셈블리어라는 저수준 프로그래밍 언어로 컴파일합니다.

어셈블리는 C보다 연산의 종류가 훨씬 적지만, 여러 연산들이 함께 사용되면 C에서 할 수 있는 모든 것들을 수행할 수 있습니다. C 코드를 어셈블리 코드로 변환시켜줌으로써 컴파일러는 컴퓨터가 이해할 수 있는 언어와 최대한 가까운 프로그램으로 만들어 줍니다. 컴파일이라는 용어는 소스 코드에서 오브젝트 코드로 변환하는 전체 과정을 통틀어 일컫기도 하지만, 구체적으로 전처리한 소스 코드를 어셈블리 코드로 변환시키는 단계를 말하기도 합니다.

 

 

어셈블(Assemble)

소스 코드가 어셈블리 코드로 변환되면, 다음 단계인 어셈블 단계로 어셈블리 코드를 오브젝트 코드로 변환시키는 것입니다. 컴퓨터의 중앙처리장치가 프로그램을 어떻게 수행해야 하는지 알 수 있는 명령어 형태인 연속된 0과 1들로 바꿔주는 작업이죠. 이 변환작업은 어셈블러라는 프로그램이 수행합니다. 소스 코드에서 오브젝트 코드로 컴파일 되어야 할 파일이 딱 한 개라면, 컴파일 작업은 여기서 끝이 납니다. 그러나 그렇지 않은 경우에는 링크라 불리는 단계가 추가됩니다.

 

 

링크(Link)

만약 프로그램이 (math.h나 cs50.h와 같은 라이브러리를 포함해) 여러 개의 파일로 이루어져 있어 하나의 오브젝트 파일로 합쳐져야 한다면 링크라는 컴파일의 마지막 단계가 필요합니다. 링커는 여러 개의 다른 오브젝트 코드 파일을 실행 가능한 하나의 오브젝트 코드 파일로 합쳐줍니다. 예를 들어, 컴파일을 하는 동안에 CS50 라이브러리를 링크하면 오브젝트 코드는 GetInt()나 GetString() 같은 함수를 어떻게 실행할 지 알 수 있게 됩니다.

 

 


 

버그와 디버깅

버그(bug) 코드에 들어있는 오류입니다. 버그로 인해 프로그램의 실행에 실패하거나 프로그래머가 원하는 대로 동작하지 않게 됩니다. 버그를 만들고 싶지 않겠지만 모든 프로그래머들은 버그와 마주하게 되어있습니다. 디버깅(debugging) 코드에 있는 버그를 식별하고 고치는 과정입니다. 프로그래머는 디버거라고 불리는 프로그램을 사용하여 디버깅을 하게 됩니다.

 

 

디버깅의 기본

프로그램은 일반적으로 인간보다 훨씬 빠르게 연산을 수행합니다. 그래서 프로그램을 실행시켜보는 것만으로는 무엇이 잘못됐는지 찾아내기 어렵습니다. 디버거는 프로그램을 특정 행에서 멈출 수 있게 해주기 때문에 버그를 찾는데 도움이 됩니다. 프로그래머는 멈춰진 그 지점에서 무슨 일이 일어나는지 볼 수 있습니다. 프로그램이 멈추는 특정 지점 중지점이라고 합니다. 또한 프로그래머가 프로그램을 한번에 한 행씩 실행할 수 있게 해줍니다. 이로써 프로그래머는 프로그램이 내리는 모든 결정들을 단계별로 따라갈 수 있게 됩니다.

 

 

GDB사용해보기

GDB는 자주 쓰이는 디버거 중 하나입니다. C 프로그램에 GDB을 실행시키려면, 먼저 프로그램을 컴파일해야 합니다. 그런 다음, 보통 때처럼 ”./프로그램_이름” 을 치지 말고, “gdb 프로그램_이름”을 칩니다.

GDB가 열리면, 가장 먼저 해야할 일은 중지점을 설정하는 것입니다. 어디에서 프로그램이 잘못되는지 짐작이 간다면, 그 지점 이전에 있는 행에 중지점을 설정하는 것이 좋습니다. 여러분의 프로그램이 문제가 생길 것이라 생각한 그 지점에 들어서면 어떤 일이 생기는지 볼 수 있기 때문입니다. 어디가 문제인지 확실하지 않다면, 처음부터 모든 코드를 살펴볼 수 있도록 여러분의 main 함수의 첫 행에 중지점을 설정해도 괜찮습니다.

 

중지점을 설정하기 위해서는 프로그램을 멈추고 싶은 행 번호 다음에 ‘b’를 치고(breakpoint를 의미) 엔터 키를 누릅니다. 이렇게 하면 여러분 프로그램에 중지점이 설정될 겁니다. 현재의 모든 중지점을 보고 싶다면 “info b”를 치면 모든 중지점의 위치를 보여줄 것입니다. 중지점의 행 번호 다음에 “clear”를 치면 중지점을 제거할 수 있습니다.

중지점을 설정했다면, ‘r’(run의 의미)로 프로그램을 실행합니다. 여러분들의 프로그램이 명령어 인자를 받는다면, ‘r’ 다음에 인자들을 쓰세요. 프로그램이 실행될 것이고, 중지점에서 자동으로 멈출 겁니다. 중지점마다 프롬프트가 나타날 것입니다. 이 때 몇 가지 옵션들이 있습니다.

현재 지점에서 프로그램의 변수값을 보고 싶다면 변수 이름 다음에 ‘p’를 입력합니다(print). “info locals” 명령어는 현재 모든 지역 변수값을 보여줄 것입니다.

코드의 다음 행으로 나아가고 싶다면 ‘n’을 입력합니다(next). ‘s’(step)를 쳐도 코드의 다음 행으로 가기는 하지만, 함수 내부로 들어가서 함수 내부의 각 행을 훑을 것입니다.

프로그램을 계속 실행하고 싶다면 ‘c’를 입력합니다(continue). 중지점이 없다면 프로그램은 종료할 것입니다. 중지점이 있다면, GDB는 다음 중지점에서 멈출 겁니다.

 

반응형