학부 수업 내용 정리/자료구조

동적할당을 왜 하는 걸까요?

supersumin 2024. 9. 28. 21:35

malloc과 calloc과 같은 동적 메모리 할당을 사용하는 이유는 프로그램 실행 중에 변화하는 변수나 데이터의 크기를 유연하게 관리할 수 있기 때문이다. 또한 한정된 메모리를 유동적으로 할당하고 해제할 수 있기 때문이다.

 

이를 자세히 알아보자.

1. 코드 실행 중 메모리가 변화?

코드 실행 중 메모리 크기가 변동하는 경우는 프로그램을 짜는 도중에 변수의 크기를 정할 수 없다.

 

예를 들어 배열의 크기가 사용자가 제시한 크기만큼 실시간으로 할당받아야 한다면 프로그램을 짜는 도중에는 배열의 크기를 정할 수 없다.

 

그렇기 때문에 사용자에 입력에 따라 변수의 데이터 크기를 다르게 받을 방법 중 하나가 동적할당인 것이다.

malloc을 사용해 동적할당을 수행한 모습이다. 이를 통해 사용자가 원하는 크기만큼 유연하게 메모리의 크기를 정할 수 있다.

 

2. 그냥 처음부터 메모리의 크기를 크게 받으면 되지 않나?

함수 내에서 메모리의 크기가 큰 변수를 선언하면 Stack에 저장되며, Stack은 제한된 크기에 메모리 공간을 가지고 있다. 그렇기에 큰 배열이나 구조체를 Stack에 할당하면 Stack의 크기를 초과하여 Stack Overflow가 발생할 수 있다.

 

또한 배열의 크기를 고정하면 사용자가 실제로 필요로 하는 크기와 맞지 않을 경우 불필요한 메모리 공간이 낭비될 수 있다. 예를 들면 100기의 요소를 정적할당 했으나 실제로는 20개만 사용하는 경우 80개의 공간이 낭비된다.

정적 할당과 동적 할당의 코드상 차이를 보여준다.

 

3. C 언어에서 동적 메모리를 할당하는 두 가지 함수! malloc, calloc!

3.1. malloc(Memory Allocation)

malloc 함수는 C 언어에서 동적 메모리를 할당하는 데 사용되는 표준 라이브러리 함수이다. 이 함수는 프로그램이 실행되는 동안 필요에 따라 메모리를 동적으로 할당할 수 있게 해준다.

- malloc의 선언

malloc 함수는 C 표준 라이브러리의 stdlib.h 헤더 파일에 다음과 같이 선언되어 있다.

- malloc의 단순화된 구현

malloc 함수의 기능은 메모리가 부족한 경우 NULL을 return하고, 메모리가 부족하지 않다면 메모리를 할당할 크기만큼 offset을 업데이트하여 포인터를 반환한다. 이 때 malloc 함수가 void* 형식의 포인터를 반환한다. 

- malloc 사용 예시

  • 포인터 변수로 선언(int* arr): 저장할 데이터의 주소를 가리키는 포인터 변수를 생성한다.

arr동적으로 할당된 메모리 블록의 시작 주소를 가리킨다. malloc이 반환하는 주소는 arr이 가리키는 메모리의 시작점을 나타내며, arr은 그 주소에 직접 접근할 수 있다.

 

메모리 블록5개의 int형 데이터를 저장하는 장소이다. 이 장소는 메모리에서 물리적으로 할당된 공간으로 arr 포인터가 가리키는 주소에 데이터가 저장된다. 예를 들어 arr[0], arr[1], ... , arr[4]는 각각 할당된 메모리 공간의 위치에 저장된 값이다.

  • malloc의 반환 값

malloc 함수는 void* 타입의 포인터를 반환한다. 그렇기 때문에 선언한 변수에 맞춰 자료형을 casting해줘야 한다. 위 코드에서는 int* 타입으로 casting하였다.

  • 함수 인자

malloc의 인자할당할 메모리의 크기이다. 여기서 단위는 Byte 단위이므로 sizeof() 함수를 사용하여 자료형이 몇 Byte인지 계산해주고 그 수에 개수만큼 곱하는 걸 확인할 수 있다.

 

3.2.calloc(Contiguous Allocation) 

C언어에서 동적 메모리를 할당할 때 사용되는 표준 라이브러리 함수이다. malloc과 유사하게 동적 메모리를 할당하지만 calloc은 메모리를 0으로 값을 초기화하여 할당한다.

- calloc 함수의 선언

calloc 함수C 표준 라이브러리의 stdlib.h 헤더 파일에 다음과 같이 선언되어 있다.

- calloc 함수의 구현

collac 함수할당할 개수, 할당할 자료형의 크기가 0이라면 NULL을 return하고, malloc함수를 사용해 void*를 return한 뒤, bzero와 같은 함수를 사용해 0으로 초기화한다.

- calloc 함수 사용 예시

malloc과 유사하게 arr은 메모리가 할당된 주소를 가리키는 변수고, calloc 또한 void*를 반환하므로 int*로 casting했다. 

 

차이점은 calloc의 함수인자동적 할당할 개수와 하나의 데이터의 크기이다.

 

3.3. free 함수

free 함수C언어에서 동적으로 할당한 메모리를 해제하는 데 사용되는 표준 라이브러리 함수이다. 사용자가 더 이상 필요하지 않은 동적 메모리 블록을 반환하여 메모리 누수를 방지한다.

- free 함수의 사용법

해제할 메모리 블록의 포인터를 함수 인자로 넣으면 해제된다.

 

free 함수가 내부적으로 메모리 블록의 크기를 참조하여 정확한 해제를 수행하는 방식을 사용하기 때문에 포인터 하나만 사용하여도 그 블록의 모든 메모리를 해제할 수 있다.

 

3.4. 2차원 동적할당 방식

2차원 배열을 동적으로 할당한다면 포인터 포인터를 사용하는 방법이 있다. 각 행의 주소를 저장하는 포인터 배열을 생성하고 그 포인터 배열의 각 요소가 다시 각각의 열을 가리키도록 하는 방법이다.

- 2차원 동적할당 예시

  • 동적 할당 하는 법

하나의 행은 열의 개수만큼의 요소를 가지는 배열이며, 그 배열들이 row만큼 모여 또 다시 배열을 이루는데 배열의 배열을 가리키는 변수를 arr로 선언한 것이다.

 

선언 방법은 row만큼 포인터 포인터로 선언하여 동적할당 한 뒤, col의 개수만큼 동적할당하는 것이다. 

  • 메모리 해제

각 행의 메모리를 먼저 해제한 뒤, 행 배열에 대한 메모리를 해제해야 한다. 

 

이 때 순서가 바뀌면 안 된다. 행 배열에 대한 메모리를 먼저 해제하면 각 행의 메모리를 해제할 때 접근할 수 없기 때문이다.

- 그림 표현