이번에는 동적 메모리 할당에 대해 알아보겠다.
동적 메모리 할당이란?
정적 메모리 할당
모든 변수, 배열, 구조체 등에 대해 프로그램 실행 전에 필요한 만큼의 변수를 선언하여 사용 -> 프로그램 실행 전에 변수의 메모리 할당 크기가 정해짐
- 컴파일 과정에서 메모리 크기가 결정되는 메모리 할당 방법
- 프로그램 실행 전에, 어떤 종류의 변수를 얼마나 많이 필요로 하는지를 개발자가 미리 알아야함
- 프로그램 실행 전에 반드시 모든 변수, 배열, 구조체를 선언해야함
문제점
- 프로그램 실행 중에 필요에 따라 메모리 할당 크기를 늘리거나 줄일 수 없음
- 프로그램 실행 전, 메모리를 얼마나 필요로 할 것인지 정확하게 알 수 없는 경우, 메모리를 효율적으로 사용하기 어려움
동적 메모리 할당
프로그램을 실행하는 동안 필요한 시점에 필요한 만큼만 메모리를 할당하는 방법
변수 이름이 없음
컴파일 과정에서는 할당할 메모리 크기를 알 수 없음
메모리를 효율적으로 사용 가능
힙(heap) 메모리 영역 사용
C 프로그래밍에서의 메모리 구성
동적 메모리 할당 절차
- 동적 메모리 할당을 위해서는 <stdlib.h> 파일을 include 선언해야함
- 동적 메모리 할당 : malloc() 함수
함수 원형 | void * malloc(unsigend int size); |
---|---|
함수 인자 | 할당 받을 메모리 크기(byte 단위, sizeof()함수 이용) |
반환 값 | 반환 값 유형 : void형 포인터 / 할당 받은 메모리의 시작 주소를 반환 / 요청한 메모리 공간을 할당할 수 없는 경우 -> NULL 반환 |
malloc()함수 사용 예
- 정수 4개를 저장할 수 있는 공간을 할당 - 일반적인 호출 형태 : malloc(4*sizeof(int)); -> 실질적인 호출 : malloc(16);
- 문자 3개를 저장할 수 있는 공간을 할당
- 일반적인 호출 형태 : malloc(3*sizeof(char));
-> 실질적인 호출 : malloc(3);
동적 메모리 사용 : 포인터 변수 사용
- 힙에 할당된 메모리 공간은 포인터 변수를 이용해서 접근하는 방법 밖에 없음
- 동적으로 할당 받은 메모리의 시작 주소를 저장하는데 사용
- 실제 포인터 변수 혹은 일반 배열명처럼 활용
- 사용 형태
- 포인터 변수 사용 예
동적 메모리 반남 : free() 함수
- 동적으로 할당 받은 메모리는 자동으로 해제되지 않음
- 더 이상 동적 할당 메모리를 사용할 필요가 없는 경우, 명시적으로 시스템에 반납(해제)해야 함
함수 원형 | void free(void * ptr) |
---|---|
함수 인자 | 해제할 메모리 공간을 가리키는 포인터 변수 |
ex)
1 | int * p = NULL; |
동적 메모리를 안전하게 사용하기
- 메모리 해제 이후, 반드시 포인터 변수를 NULL로 초기화
-> 댕글링 포인터(dangling pointer) 방지
댕글링 포인터 : 여전히 해제된 메모리 영역을 가리키고 있는 포인터
- malloc()을 호출한 후에는 그 반환 값을 검사하여 메모리 할당의 성공 여부를 확인하는 부분이 필요
-> NULL포인터를 반환하는 이유 : 요청한 메모리 크기만큼 연속된 메모리 공간을 확보하지 못할 때
- 일반적으로 다음과 같이 메모리 할당의 성공 여부를 검사
1 | int * p = NULL; |
기타 동적 메모리 할당 함수
- calloc() 함수
함수 원형 | void* calloc(unsigned int num, unsigned int size); |
---|---|
함수 인자 | num : 동적으로 할당 받을 원소의 개수 / size : 원소 한 개의 크기 (바이트 단위) |
반환 값 | 반환 값의 유형 : void형 포인터 / (num*size) 바이트 수 만큼 할당하고, 0으로 초기화 후 시작 위치 반환 / 요청한 메모리 공간을 할당할 수 없는 경우 -> NULL 반환 |
ex)
1 | int *p = NULL; |
- realloc() 함수
함수 원형 | void* realloc(void * ptr, unsigned int size); |
---|---|
함수 인자 | ptr : 확장할 메모리의 시작 주소 / size : 확장할 메모리의 전체 크기 ( 바이트 단위 ) |
반환 값 | 반환 값의 유형 : void형 포인터 / ptr이 가리키는 메모리의 크기를 size 바이트 크기로 조정하고 시작 위치 반환 / 요청한 메모리 공간을 할당할 수 없는 경우 -> NULL 반환 |
- realloc() 함수의 3가지 경우
1) 기존 메모리 주소를 기반으로 메모리 크기를 줄이는 경우
기존에 입력되어 있던 데이터는 잘려나감
기존 메모리 주소와 동일한 주소가 반환됨
- 기존 메모리 주소와 동일한 주소가 반환됨
새로운 메모리를 할당하고, 기존 메모리 내용을 복사함
이전 메모리 주소(p1)는 자동 해제됨
기존 메모리 주소와 다른 주소(p2)가 반환됨
malloc() vs calloc() vs realloc()
- malloc() : 메모리 할당
- calloc() : 메모리 할당 + 메모리 초기화
- realloc() : 기존에 할당된 메모리 크기를 자유롭게 조절