동적 메모리 할당(Dynamic Memory Allocation)
- 입력 되는 데이터에 맞게 기억공간을 확보하는 것
정적 메모리 할당(Static Memory Allocation)
- 프로그램을 작성하는 단계에서 필요한 기억공간의 크기를 결정하는 것
우리는 일반적으로 배열을 선언할때 정적 메모리 할당을 사용한다.
char examples[10];
이렇게 하면, char 자료형 10개가 나란히 선언되고, examples 배열에는 10개의 자료형 밖에 못들어간다.
왜 동적 메모리 할당을 해야하는가?
정적 메모리 할당을 하게되면, 기억공간이 낭비될 경우가 많다. 예를 들어, 위의 선언한 char형 배열 examples에 만약 "hello"만 넣는다면, 5바이트가 또 낭비되는 셈이다. 고로, 메모리 낭비를 최소화 하기위해서는 프로그램의 실행중에 입력되는 데이터에 맞게 기억공간을 할당할 필요가있다.
혹시 이런 코드는 안될까?
int stNum;
printf("학생수를 입력해주세요")
scanf("%d",&stNum);
int arScore[stNum];
언뜻 보기엔 이 코드가 동작할 것 같지만 컴파일 해보면 에러가 발생한다.
--> 배열 선언문의 크기값은 변수로 지정할 수 없고 stNum이라는 변수값은 실행중에 사용자가 입력하는 값이기 때문에 컴파일 할 때는 이 값이 얼마가 될지 알수없다.
메모리 필요량을 프로그램 작성시에 전혀 예측할수없는 경우도있다. ex) 임의의 학교에 대해서 학생들의 성적을 처리하는데 사용할 수 있는 일반적인 응용프로그램을 예로 들면 학생이 10명도 채 안되는 시골 학교도 있는 반면 수만명이나 되는 경우도 있을 것 이다. 그렇다고 해서 배열 크기를 100만 정도로 충분하게 정적 할당하는 것은 메모리를 지나치게 낭비하게 되므로 좋지않다.
* 중요 - 동적 할당이란 필요할 때 필요한만큼만 메모리를 할당해 사용하고 다 쓰면 버리는 것 이다. 동적 할당된 메모리는 이름이 없는 변수라고 할수 있으며 독점적인 메모리 영역을 차지하고 있어 일단 값을 기억할 수 있지만 이름이 없으므로 오로지 포인터로만 접근할 수 있다. 그래서 malloc 함수가 리턴하는 포인터는 반드시 적절한 타입의 포인터 변수로 대입받아야한다.
시작번지를 잃어버리면 할당된 메모리를 쓸 수 없음은 물론이고 다 사용하고 난 후에도 free함수로해제하지 못한다.
메모리 관리 원칙
- 메모리의 실체는 시스템에 장착되어 있는 RAM이다 복수 개의 프로그램이 꼭 필요한 만큼의 메모리를 충돌없이 사이좋게 잘 사용하려면 정교한 메모리 관리 원칙이 필요하다.
1. 메모리 관리의 주체는 운영체제이다.
2. 운영체제는 메모리가 있는 한은 할당 요청을 거절하지 않는다.
3. 한 번 할당된 메모리 공간은 절대로 다른 목적을 위해 재할당되지 않는다.
4. 응용 프로그램이 할당된 메모리를 해제하면 운영체제는 이 공간을 빈 영역으로 인식하고 다른 목적을 위해 사용할 수 있도록 한다.
동적 메모리 할당 및 해제 방법
메모리를 동적으로 할당 및 해제할 때는 다음 두함수를 사용한다.
void *malloc(size_t size);
void free(void *memblock);
malloc 함수
인수로 필요한 메모리양을 바이트 단위로 전달하면 요청한만큼 할당한다. ex) 10바이트 필요 - malloc(10) 실행중에 할당하는 것이므로 malloc(Num)과 같이 변수도 사용할 수 있다. malloc은 응용 프로그램이 필요로 하는 양만큼 운영체제에게 할당을 요청하며 운영체제는 사용되지 않는 빈 영역(힙)을 찾아 요청한만큼 메모리를 할당하여 그 시작 번지를 리턴한다. 응용 프로그램이 할당한 메모리를 어떤 목적으로 사용하는지 알 수 없으므로 malloc은 void 형을 리턴하며 받는 쪽에서 원하는 타입으로 바꿔야한다.
free 함수
동적으로 할당한 메모리를 해제한다. 응용 프로그램은 메모리를 다 사용한 후에 반드시 free함수를 호출하여 메모리를 해제해야 한다. 그래야 이 영역을 재활용할 수 있다.
예시 코드
int *ar;
ar=(int *)malloc(10*sizeof(int));
// ar사용
free(ar);
* 동적으로 할당된 메모리를 사용하려면 그 시작 번지를 기억해야 하므로 포인터 변수가 필요하다. malloc은 리턴타입이 void라고 했잖아 그래서 이 포인터를 변수에 대입할 떄는 반드시 원하는 타입으로 캐스팅할 필요가 있어. 그래서 int *ar; 로 정수형 배열로 캐스팅함 즉 포인터 ar이 가르키는 번지는 마치 ar[10] 정수열과 같아진다. 다 사용하고 난후에는 반드시 free함수로 메모리를 해제한다.
* malloc 함수는 할당에 실패하면 에러의 표시로 NULL을 리턴한다.
* 그러나 32비트 운영체제는 메모리를 할당했던 프로그램이 종료하면 해제하지 않은 메모리를 알아서 회수한다 16비트는 안함
또다른 할당함수 calloc함수
malloc 함수와 마찬가지로 메모리를 할당하되 필요한 메모리양을 지정하는 방법만 다른 함수이다.
void *calloc(size_t num, size_t size);
첫번쨰 인수 num은 할당할 요소의 개수이고 size는 요소의 크기이다.
malloc과 다른점
malloc은 필요한 메모리를 바이트 단위 하나로만 전달받지만 calloc은 두 개의 값으로 나누어 전달받는다. malloc이 몇 바이트 할당 "해줘"라고 요청하고 calloc은 몇 바이트짜리 몇 개 할당 "해줘"라고 요청하는거
ar = (int*)malloc(10*sizeof(int));
ar = (int*)calloc(10,sizeof(int));
그래서 두 호출문은 동일하다.
또한 calloc은 메모리 할당 후 전부 0으로 초기화한다. malloc은 메모리 할당만 하므로 할당된 메모리에는 쓰레기 값이 들어 있지만 calloc으로 할당 하면 할당 후 모든 메모리를 0으로 채운다. 즉 할당 후에 배열을 바로 초기화 해야한다면 malloc 호출 후 memset을 사용할 수도 있지만 이 방법보다는 calloc 함수로 할당하는 것 이 더 편리하다.
재할당 방법, realloc
realloc은 이미 할당된 메모리의 크기를 바꾸어 재할당한다.
void *realloc(void *memblock, size_t size);
첫번째 인수로 malloc이나 calloc으로 할당한 메모리의 시작 번지를 주고 두번째 인수로 재할당할 크기를 전달한다. 만약 첫번째 인수가 NULL인 경우, 즉 할당되어 있지 않을 경우는 새로 메모리를 할당하므로 realloc의 동작은 malloc과 같아진다.
두 번째 인수 size가 0일 경우 할당을 취소하라는 애기이므로 free와 같아진다.
realloc은 신인가?
예제 코드
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *ar;
ar = (int *)malloc(5*sizeof(int));
ar[4] = 1234;
ar = (int *)realloc(ar,10*sizeof(int));
ar[9]=5678;
printf("ar[4]=%d, ar[9]=%d\n",ar[4],ar[9]);
free(ar);
}
실행 결과는 ar[4] = 1234, ar[9] = 5678이다.
* 크기를 확장하면 ar의 번지는 바뀔 수는 있지만 원래 배열에 들어 있던 모든 값은 그대로 유지된다.
동적 할당이 필요한 이유는 컴파일할 시점에 필요한 메모리양을 모를 때가 있기 때문이다. (malloc)
재할당이 필요한 이유는 실행중에라도 필요한 메모리양을 가늠할 수 없을 때가 있기 때문이다. (realloc)
c++에는 생성자와 파괴자도 있대
출처:https://tyeolrik.github.io/c/2017/02/01/1-dynamic-memory-allocation.md.html
'컴퓨터 시스템' 카테고리의 다른 글
메모리 구조 (0) | 2023.05.12 |
---|---|
프로그램의 기계수준 표현, 링커 (0) | 2023.05.12 |
Red-Black 트리 (0) | 2023.05.07 |
Ubuntu 리눅스 명령어 정리 (0) | 2023.05.04 |
Chapter 1 컴퓨터 시스템으로의 여행 (0) | 2023.05.02 |