2024. 10. 8. 08:57ㆍCS/ServerProgramming
1. make
make는 여러 개의 소스코드 그룹을 관리하는 툴이다.
make를 강력하게 추천하는 이유는 incldue 로 다른 로컬 헤더파일을 포함하거나, 복잡한 구조를 쉽게 관리할 수 있다.
특히 여러 개의 소스코드 중 딱 하나만 수정한 경우 수정된 파일만 새로 컴파일하여 굉장히 효율적으로 소드코드 그룹을 관리할 수 있다.
make는 Target, dependency, command로 이루어져 있다. File Foramt은 다음과 같다.
target: dependency
[tab] command
make 를 입력하면 Makefile을 실행하고, target을 생성한다. target 을 빌드할 때 필요한 파일들이 dependency에 적혀있고, target을 생성하기 위해 실행할 명령어가 command에 적히게 된다.
make example 및 컴파일 과정
컴파일 과정을 짚어보고 넘어가자.
- 전처리
- 소스 코드에서 #으로 시작하기 전처리기 지시문을 처리한다.
- 헤더 파일을 포함시킨다.
- gcc -E : 전처리만 수행한다.
- 컴파일
- 전처리된 코드를 어셈블리 코드로 변환한다.
- 문법 검사, 타입 체킹 등을 수행한다.
- gcc -S : 어셈블리 코드 생성까지만 수행한다.
- 어셈블
- 어셈블리 코드를 기계어로 변환한다.
- 결과물로 .o 파일, 즉 오브젝트 파일이 생성된다.
- gcc -c : 오브젝트 파일 생성까지만 수행한다.
- 링킹
- 여러 오브젝트 파일을 하나의 실행 파일로 결합한다.
- 라이브러리 함수를 연결한다.
- 실행 가능한 프로그램을 생성한다.
make를 사용하지 않고 main.c, add.c, sub.c를 컴파일하고 링킹하려면 다음과 같은 과정이 필요하다.
- gcc -c main.c
- gcc -c add.c
- gcc -c sub.c
- gcc -o test main.o add.o sub.o
- 혹은, gcc -o test main.c add.c sub.c
Makefile을 만들어서 다음과 같은 형태로 사용하면 굉장히 편리하다.
all: main.o add.o sub.o
gcc -o test main.o add.o sub.o
main.o: addsub.h main.c
gcc -c main.c
add.o: add.c
gcc -c add.c
sub.o: sub.c
gcc -c sub.c
위의 Makefile이 존재하는 디렉토리에서 make 명령어를 입력하면 all 타겟을 생성한다.
all 타겟을 생성하려고 의존성을 보니, main.o, add.o, sub.o가 존재하므로 해당 target을 찾아서 command를 실행하여 의존성을 해결하는 과정으로 명령이 이루어진다.
target이 다시 컴파일 되는 경우는 다음과 같다.
- main.c가 수정된 경우
- add.c, sub.c가 수정된 경우
- addsub.h가 수정된 경우
make clean
all: main.o add.o sub.o
gcc -o test main.o add.o sub.o
main.o: addsub.h main.c
gcc -c main.c
add.o: add.c
gcc -c add.c
sub.o: sub.c
gcc -c sub.c
clean:
rm *.o test
위와 같은 방식으로 오브젝트 파일과 실행 파일을 제거할 수 있다.
내부변수 사용
CC=gcc
SRC=main.c add.c sub.c
main: ${SRC}
${CC} -o test ${SRC}
아까의 예제를 내부변수를 사용하여 변경해줄 수 있다. 내부변수를 사용해서 관리하는 편이 훨씬 좋다.
미리 정해져있는 내부변수가 몇 가지 있다. 주로 사용되는 세 가지만 알아두자.
- CFLAGS : gcc 옵션 세팅
- CC = cc (= gcc)
- CPPFLAGS : G++ 옵션
- CXX = g++
- LDFLAGS : ld(linker) 옵션 세팅
- LS = ld
2. Makefile Standard
CFLGS = -Wall -O -g
bin=hello
t1=main
t2=funcs
obj=$(t1).o $(t2).o
all: $(bin)
$(bin) : $(obj)
$(CC) $(obj) -o $@
clean:
rm -f $(bin) *.o
- $@ : 만들려는 target 여기서는 $(bint) 을 나타내는 내부변수이다.
- -O : 최적화
- -g 디버깅
- -Wall : 모든 오류를 알려줘.
3. GDB 기본 사용
gcc 컴파일시 -g 옵션을 추가하고 -O 옵션을 제거해서 디버깅 할 수 있다.
gdb a.out 혹은 gdb --args a.out 1 2 3 4와 같이 gdb를 실행할 수 있다.
기본 커맨드를 알아보자.
종료
- q / ctral+d
소스 찾아가기
- l : main 함수 기점으로 소스 내용 출력
- l 10 : 10행 주변 소스 출력
- l func : func 함수 소스 출력
- l a.c:func : a.c 파일의 func 함수 출력
- l a.c:10 : a.c 파일의 10행 기준 출력
중단점
- b func : func 함수에 브레이크 포인트 설정
- b 10 : 10행에 브레이크 포인트 설정, 10행은 실행되지 않음.
- b a.c:10 : a.c파일의 10행에 브레이크 포인트 설정
- b 10 if var == 0 : 10행에 브레이크 포인트를 설정하되, var 변수 값이 0일 때 작동
중단점 삭제하기
- cl func : func 함수의 시작 부분에 브레이크 포인트 삭제
- cl : 모든 브레이크 포인트 삭제
프로그램 실행, 종료
- r : 프로그램 실행
- r arg1 arg2 : 프로그램 실행 시 파라미터로 arg1, arg2를 줌
- k : 프로그램 종료
역추적하기
- bt : 오류가 발생한 함수를 역으로 찾아감
변수 정보보기
- info locals : 현재 상태에 존재하는 지역 변수와 값 보기
- p laval : laval 값 확인
- p pt : pt가 구조체라면 구조체의 주소 확인
- p *pt : pt가 구조체라면 구조체의 값 확인
디버깅 하기
- s : 현재 행을 수행하고 멈추지만, 함수의 경우 함수 내부로 들어감.
- n : 현재 행을 수행하고 멈추지만, 함수의 경우 함수를 수행하고 넘어감
- c : 다음 브레이크 포인트를 만날때 까지 계속 수행
- u : for문에서 빠져나와 다음 브레이크 포인트까지 수행
4. gdb 실습
실습 1
j=j*i;에 중단점을 두고 실행하면서 j의 값을 찍어보니 쓰레기 값이 들어있는 것을 확인. j를 초기화하지 않고 사용함!
실습 2
temp[0]='F' 에서 오류가 생김. 원인을 생각해보면 문자 포인터에 문자열을 저장하면, 문자열이 상수 리터럴 형태로 저장되므로 변경 불가능.
실습 3
strcpy 에 중단점을 설정하고 실행한 뒤, buf 값을 찍어보면 0x0이 나옴. malloc에 실패한 것이고 1<<31은 굉장히 큰 값, 2GB이므로 실패함.
실습 4
scanf에 문자열을 입력하면 다음 입력을 받지 않고 계속해서 실행됨. if (inp == 0) 에 중단점을 두고 inp의 값을 찍어보니 scanf를 호출하지 않고 계속 0으로 차있음.
문자를 입력하면 scanf가 실패하지만, 입력 버퍼에 그 문자가 남아있어서 다음 scnaf에 입력버퍼의 값이 들어가서 scanf를 호출하지 않음. 따라서 getchar() 로 입력 버퍼를 비워주면 해결됨.
실습 5
i가 2일때 3.14를 잘 출력하고, i가 3일때 stack smashed가 감지되어 종료된다. 당연히 배열의 범위를 넘어서 그렇다
'CS > ServerProgramming' 카테고리의 다른 글
[ServerProgramming] Vi Editor, GCC (0) | 2024.10.07 |
---|---|
[ServerProgramming] UNIX의 BASIC COMMAND를 배워보자! (0) | 2024.10.05 |
[ServerProgramming] 운영체제 복습 - 프로세스와 스레드 (0) | 2024.10.04 |