[UNIX] Makefile, gcc, 메모리

Taegang Yun·2023년 9월 13일
1

Unix 프로그래밍

목록 보기
2/19

컴파일 환경

컴파일

텍스트로 작성한 프로그램을 시스템이 이해할 수 있는 기계어로 변환하는 과정

  • 컴파일을 한다 = 보통 컴파일 과정과 라이브러리 링크 과정을 하나로 묶어서 수행하는 것
  • 서로 다른 파일에 흩어져 있던 함수나 클래스들을 한 데 묶어서 링크해주는 작업
gcc main.o foo.o bar.o -o a.out
  • main 함수 안에 foo 함수와 bar 함수가 어디에 정의되어 있는 지 위치를 찾고 제대로 함수를 호출하 수 있게 된다.

GNU C 컴파일러 : gcc

  • gcc

기능 = C 프로그램을 컴파일해 실행 파일을 생성
형식 = gcc [옵션][파일명]
옵션
-c : 오브젝트 파일(.o)만 생성
-o 실행 파일명 : 지정한 이름으로 실행 파일을 생성, 기본 실행 파일명은 a.out
사용 예:

$ gcc test.c 
$ gcc -c test.c 
$ gcc -o test test.c

[예제]

$ gcc ch1_2.c
$ ls

a.out ch1_2.c
  • 파일명을 별도로 지정하지 않았으므로 a.out이라는 이름으로 실행 파일이 생성
$ gcc –o ch1_2.out ch1_2.c
$ ls

a.out ch1_2.out ch1_2.c
  • 실행 파일명을 ch1_2.out이라 하려면 -o 옵션을 사용

  • 실행 파일명을 입력하면 프로그램이 실행

  • 현재 디렉터리(.)가 경로에 설정되어 있지 않다면 현재 디렉터리를 지정해 실행

  • 이후에는 현재 디렉터리가 경로에 있다고 가정하여 ./를 표시하지 않음.

$ ch1_2.out (현재 디렉터리가 경로에 있을 경우)
(또는) 
$ ./ch1_2.out (현재 디렉터리가 경로에 없을 경우

Makefile과 make

  • Makefile : 컴파일 명령, 소스 파일을 컴파일하는 방법, 링크할 파일, 실행 파일명 등을 설정 하는 파일
  • make 명령 : Makefile을 읽고 이 파일에서 지정한대로 컴파일을 실행해 실행 파일을 생성
$ sudo apt install make

Makefile은 대상(Target), 의존 관계(Dependency), 명령(Recipe)의 세 가지로 이뤄진다.

<Target> : <Dependency>
(tab)<Recipe>

대상

빌드 대상 이름. 명령에 의해 생성되는 결과 파일, 오브젝트 파일이나 실행 파일

  • make 의 대상: 무엇이 make를 통해 최종적으로 만들어지는가?
  • 예 : make a.out make libft.a make all

의존 관계

대상을 만들 때 의존되는 파일들. 여기에 나열된 대상들을 먼저 만들고 빌드 대상을 생성한다.

레시피

주어진 타겟을 make 할 때 실행할 명령어들의 나열

  • 반드시 탭 한 번으로 들여쓰기를 해줘야 한다.

Makefile 기본 패턴

CC = <컴파일러>
CFLAGS = <컴파일 옵션>
LDFLAGS = <링크 옵션>
LDLIBS = <링크 라이브러리 목록>
OBJS = <Object 파일 목록>
TARGET = <빌드 대상 이름>

all : $(Target)

clean : 
	rm -f *.o
    rm -f $(TARGET)

$(TARGET) : $(OBJS)
$(CC) -o $@ $(OBJS)

[예시]

 # Makefile

 CC=gcc
 CFLAGS=
 OBJS=ch1_3_main.o ch1_3_addnum.o
 LIBS=
 all : add.out

 add.out : $(OBJS)
     $(CC) $(CFLAGS) -o add.out $(OBJS) $(LIBS)

 ch1_3_main.o : ch1_3_main.c
     $(CC) $(CFLAGS) -c ch1_3_main.c
 ch1_3_addnum.o : ch1_3_addnum.c
     $(CC) $(CFLAGS) -c ch1_3_addnum.c

 clean:
     rm -f $(OBJS) add.out *.o core
  • 클린(Clean) 매크로

    • rm -f

    • 빌드 결과물(ex. a.out)과 중간 부산물들(*.o)을 모두 삭제하여 깨끗한 상태에서 다시 빌드할 수 있는 환경을 만들어준다.

    • rm - rf

    • recursive로 모든 파일 삭제.

오류 메시지 출력 : perror()

#include <stdio.h>

void perror(const char *s)
  • s : 출력할 문자열

  • perror() 함수의 특징

    • 실행 파일과 오브젝트 파일을 모두 삭제하려면 make clean을 수행
    • perror() 함수는 errno 에 저장된 값을 읽어 이에 해당하는 메시지를 표준 오류(파일 기술자 2번)로 출력
    • perror() 함수의 인자로는 일반적으로 프로그램 이름을 지정하는 것이 좋음

[예시]

 #include <stdio.h>
 #include <unistd.h>
 #include <errno.h>
 #include <stdlib.h>

 int main() {
     if(access("test.txt", R_OK) == -1) {
         perror("test.txt");
         exit(1);
     }
 }

[결과]

$ ch1_4.out

test.txt: No such file or directory

오류 메시지 출력 : strerror()

#include <string.h>

char *strerror(int errnum);
  • errnum : errno 에 저장된 값
  • 함수의 인자로 errno에 저장된 값을 받아 오류 메시지를 리턴
  • 리턴된 오류 메시지를 사용자가 적절하게 가공할 수 있음

[예시]

 #include <stdio.h>
 #include <unistd.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>

 extern int errno;

 int main() {
      char *err;

      if(access("test.txt", R_OK) == -1) {
           err = strerror(errno);
           printf("오류: %s(test.txt)\n", err);
           exit(1);
      }
 }

[결과]

$ ch1_5.out

오류: No such file or directory(test.txt)

메모리 할당 : malloc()

#include <stdlib.h>

void *malloc(size_t size);
  • size : 할당받을 메모리 크기

  • 인자로 지정한 크기의 메모리를 할당하는 데 성공하면 메모리의 시작 주소를 리턴

  • 만약 메모리 할당에 실패하면 NULL 포인터를 리턴

  • 인자로 지정하는 메모리 크기는 바이트 단위

  • 할당된 메모리에는 어떤 형태의 데이터도 저장할 수 잇음

  • malloc() 함수는 할당된 메모리를 초기화하지 않는다

char *ptr;
ptr = malloc(sizeof(char) * 100);

메모리 할당 : calloc()

#include <stdlib.h>

void *calloc(size_t nmemb, size_t size);
  • nmemb : 배열 요소의 개수

  • size : 할당받을 메모리 크기

  • calloc() 함수는 nmemb * sizze 바이트 크기의 배열을 저장할 메모리를 할당

  • calloc() 함수는 할당된 메모리를 0으로 초기화

  • ex) 요소가 10개이고 각 요소의 크기가 20바이트인 배열을 저장할 수 있는 메모리를 할당

char *ptr
ptr = calloc(10, 20);

메모리 할당 : realloc()

#include <stdlib.h>

void *realloc(void *ptr, size_t size);
  • ptr : 할당받은 메모리를 가리키는 포인터

  • size : 할당받은 메모리 크기

  • realloc() 함수는 이미 할당받은 메모리에 추가로 메모리를 할당할 떄 사용

  • 이전에 할당받은 메모리와 추가할 메모리를 합한 크기의 메모리를 새롭게 할당하고 주소를 리턴

  • 이때 이전 메모리의 내용을 새로 할당된 메모리로 복사

  • ex) malloc() 함수로 할당받은 메모리에 추가로 100바이트를 할당하는 예

char *ptr, *new;
ptr = malloc(sizeof(char) * 100);
new = realloc(ptr, 100);

메모리 해제 : free()

#include <stdlib.h>

void free(void *ptr);
  • ptr : 해제할 메모리 주소
  • free() 함수는 사용을 마친 메모리를 해제하고 반납
  • free() 함수가 성공하면 ptr이 가리키던 메모리는 더 이상 의미가 없다.

명령행 인자

명령행

  • 리눅스 시스템에서 사용자가 명령을 입력하는 행
  • 프롬프트가 나타나고 커서가 사용자 입력을 기다리고 있는 행

명령행 인자 (CLA)

  • 사용자가 명령행에서 명령을 실행할 때 해당 명령(실행 파일명)과 함께 지정하는 인자
  • 명령행 인자는 명령의 옵션, 옵션의 인자, 명령의 인자로 구성

명령행 인자의 전달

main() 함수에서 명령행 인자를 전달받으려면 다음과 같이 정의

int main(int argc, char *argv[]){ ... }

[예시]

#include <stdio.h>

int main(int argc, char *argv[]){
	int n;
    
    printf("argc = "%d\n", argc);
    for(n = 0; n < argc; n++)
    	printf("argv[%d] = %s\n", n, argv[n]);
}

[결과]

$ ch1_6.out -h 2000

argc = 3
argv[0] = ch1_6.out
argv[1] = -h
argv[2] = 2000
  • 명령행에서 실행 파일명인 ch1_6.out 외에 -h와 2000을 인자로 입력
  • 따라서 main() 함수에 전달된 총 개수를 나타내는 argc 값은 3
  • argv[0]에 실행 파일명이 저장되고, 차례로 인자가 저장됨을 알 수 있음
  • argv로 전달되는 값은 문자열이므로 printf() 함수로 출력하려면 형식 지정자 %s를 사용해야 함.
profile
언젠간 전문가가 되겠지

0개의 댓글