[C언어] 동적 메모리 할당 개념 잡기

채상엽·2023년 4월 9일
5

SW사관학교 정글

목록 보기
25/35
post-thumbnail

동적 메모리 할당(Dynamic Memory Allocation)

동적 메모리 할당이란 컴퓨터 프로그래밍에서 실행 중(런타임)에 사용할 메모리 공간을 할당하는 것을 의미한다.

프로그램이 실행되기 위해서는 메모리가 필요하다. 컴파일러는 컴파일 시점에 소스 코드를 읽고, 변수 타입들의 크기에 따라 메모리를 할당한다.

이처럼 프로그램이 실행되기 전, 컴파일 시점에 소스 코드를 읽고 메모리 공간을 확보하는 것을 정적 할당(static allocation) 이라고 한다.

그렇다면 동적 할당(dynamic allocation)이란 무엇일까? 컴파일 타임이 아닌 프로그램이 실행되는 중인 런타임에 필요한 만큼의 메모리 공간을 확보하는 것을 의미한다.

동적 할당이 필요한 이유?

그렇다면 동적 할당이 필요하는 이유는 무엇일까? 그때 그때 필요할때마다 새로운 메모리 공간을 할당하는 것이 아니라, 컴파일 타임에 미리 넉넉한 메모리 공간을 확보해두면 되지 않을까? 라는 생각을 해볼 수 있다.

물론 가능은 하다. 그러나 메모리란 무한한 자원이 아니며, 한정 되어 있다. 만약 우리가 100000000 byte 사이즈의 메모리를 할당해두고 실제로는 10 byte만 사용한다면, 남은 메모리 공간을 비효율적으로 사용하게 된다.

동적할당이 필요한 이유는 그때그때 필요한 만큼만 메모리 공간을 확보하고, 다 사용했다면 free 시켜 줌으로써 메모리 공간을 해제함으로서 한정된 메모리 공간을 효율적으로 사용할 수 있게 되는것이다.

이는 함수가 종료되거나 변수의 영역을 벗어나면 자동으로 메모리 해제가 이루어지는 스택과는 다르다. 그렇다면 동적 할당은 스택이 아닌 어디에서 메모리 할당과 해제가 일어날까?

힙(heap) 영역

힙(heap) 이란 C언어나 자바와 같은 프로그래밍 환경에서 원시 자료형(primitive type)이 아닌 보다 큰 크기의 데이터를 담고자 동적으로 할당하는 메모리 공간을 지칭한다.

정리해보자면 c언어에서 말하는 동적 할당(dynamic allocation)이란 힙(heap) 영역에 필요할 때마다 메모리 공간을 할당하고, 더 이상 필요하지 않을 경우 메모리를 해제해주는 과정을 의미한다. 이를 통해서 다음과 같은 장점을 취할 수 있다.

장점

  • 상황에 따라 원하는 크기 만큼의 메모리가 할당되므로 경제적이다.(malloc or calloc)
  • 이미 할당된 메모리라도 언제든 크기를 조정할 수 있다(realloc)

단점

  • c언어의 경우 GC(Garbage Collector)가 없기 때문에, 개발자가 직접 명시적으로 메모리를 해제해주어야한다.(free)
  • 만약 이를 하지 않았을 경우, 메모리 누수가 나타나고 이는 디버깅 하기 매우 까다롭다.

메모리 할당

malloc 또는 calloc을 호출하게 되면 힙 영역에 필요한 만큼의 메모리 공간을 확보하고 이후 반환 타입으로 해당 메모리 공간의 시작 위치를 포인터로 반환한다. 또한 할당된 메모리를 어떤 목적에 사용할지 함수에서 판단하기 어렵기 때문에, return 타입은 void * 형을 return 하며, 반환 받는 쪽에서 타입 캐스팅을 통해 사용해야한다.

malloccalloc 의 차이

각각의 선언 방법에 대한 차이는 아래와 같다

int *arr=(int *)malloc(10*sizeof(int));
int *arr=(int *)calloc(10,sizeof(int));

malloc은 매개변수로 입력한 크기 만큼을 그대로 할당하는것이고, calloc은 두번째 매개변수의 크기를 첫번째 매개변수 갯수 만큼을 할당해 달라는 식으로 요청한다. 위 두 명령어 모두 똑같은 크기의 공간을 확보한다.

두 명령어의 차이는 초기화 값에 그 차이가 있다. malloc의 경우에는 메모리 공간만 할당하므로, 초기화되지 않은 메모리 공간에는 쓰레기 값들이 들어있다. 그러나 calloc 같은 경우에는 할당 후 해당 메모리 공간을 0으로 초기화한다.

따라서 성능 자체는 초기화를 시키지 않는 malloc이 조금 더 낫다. 그러나 상황에 따라서 초기화가 필요할 경우는 calloc을 선택하는것도 좋은 방법이 될 것 같다.

메모리 해제

앞서서 확인한 메모리 할당 명령어인 malloc 또는 calloc을 이용해서 메모리 공간을 할당하였고, 원하는 동작들을 모두 수행했다면 할당한 메모리 공간을 해제해주어야 한다.

처음 말했던 것처럼 우리의 메모리 공간은 한정적이며, 더 이상 사용되지 않을 메모리 공간이라면 해당 메모리 공간을 할당 해제 해줌으로써 다른 프로그램이 해당 공간을 재활용 할 수 있게 된다.

c언에서는 free 함수를 이용해 특정 주소의 메모리 공간을 할당 해제 해줄 수 있다.

메모리 누수란?

메모리 누수란 동적으로 할당한 메모리가 할당 해제(free) 될 수 없는 상태가 된 것을 의미한다.

결국 위와 같은 상황들이 반복되면 동적으로 할당된 메모리가 해제되지 못하고 계속 남아있게 되기 때문에, 결국 시스템의 메모리가 부족해져 운영체제가 메모리 할당에 실패하여 프로그램을 종료시킨다.

메모리 누수의 간단한 예시는 아래와 같다.

int *a = malloc(5); // 5byte 메모리 공간 선언
int *b = malloc(10); // 10byte 메모리 공간 선언

b = a; // b가 a의 주소를 가리킴

free(a); // a할당 해제
free(b); // b도 a의 주소를 가리키므로, a할당 해제

위와 같은 상황이라면, b는 결국 힙 영역에 해제되지 않고 계속 남아있게 된다. 이러한 상황을 바로 메모리 누수(Memory leak)라고 한다.

이를 방지하기 위해서는 malloc 또는 calloc을 사용했다면, 의식적으로 free를 사용하는 습관을 들여야 하며, 동적 메모리할당을 한 포인터의 주소를 직접 바꾸는 코드는 작성하지 않는 것이 좋다.

메모리 재할당

이미 할당되어 있는 메모리 공간의 크기를 변경하여 재할당 하는 것을 의미한다.

재할당은 realloc 함수를 이용해 메모리 공간 재할당이 가능하다. realloc의 함수 시그니처는 아래와 같다.

void *realloc(void *ptr, size_t size);

이를 해석해보면 ptr 위치에 해당하는 메모리 공간의 블록의 크기를 size로 조절한다는 의미가 된다.

  • ptr == NULL일 경우
    • 재할당을 위한 메모리 공간이 선언되어 있지 않는 것이므로, malloc 또는 calloc을 이용해 새로운 메모리 공간을 할당하는것과 동일한 기능을 수행하게 된다.
  • size == 0일 경우
    • 현재 ptr 위치의 메모리 블록 크기를 0으로 만든다는 의미이므로, 이는 곳 해당 메모리 공간을 할당 해제 한다는 의미와 같다. 따라서 이는 free 함수를 호출하는 것과 동일한 기능을 수행하게 된다.
  • ptr != NULL일 경우
    • 현재 ptr 위치에 할당되어 있는 메모리가 있고, 이 메모리 블록의 크기를 size로 변경하기 위한 동작을 수행한다.
    • 기존에 ptr 다음 블록이 이어서 위치한 상태라면, ptr이 해당 위치에서 메모리 공간의 크기를 늘리는것은 어렵기 때문에, 어쩔 수 없이 기존 ptr 블록의 내용을 유지한 채로 확장된 메모리 블록을 새로운 주소에 할당받게 된다.
    • 재할당은 일반적으로 기존 블록 크기보다 크게 할당되는것이 보편적이긴 하다. 그러나 반대로 기존 블록보다 작게 할당되는 경우도 가능하다. 만약 기존에 8byte 크기만큼의 블록을 4byte 만큼으로 축소시키려고 한다면, 재할당하는 4byte블록의 내용은 기존 8byte 블록의 4byte까지의 내용으로 채워지게 된다.

참고

profile
프로게이머 연습생 출신 주니어 서버 개발자 채상엽입니다.

0개의 댓글