c언어 동적 메모리 할당하는 이유
프로그램이 실행되려면 메모리가 필요하다. 실행 파일이 메모리에 로드되어야 실행될 수 있고, 또 프로그램에서 선언하는 변수들도 메모리에 할당된다.
int a;
double b;
컴파일러는 변수들이 값을 제대로 보관하도록 변수 타입에 맞는 크기만큼 메모리를 할당한다.
변수 a는 정수형이므로 4바이트, 변수 b는 실수형이므로 8바이트가 할당된다.
이렇게 미리 필요한 메모리량을 알려주는 할당을 정적할당이라고 한다.
프로그램을 작성할 때 필요한 메모리량을 지정하는 정적할당과는 달리,
실행 중에 필요한만큼 메모리를 할당하는 기법을 동적할당이라고 한다.
메모리를 동적으로 할당하면 메모리 관리에 효율적이라고 하는데, 예를 들어보면
100개의 정보를 저장할 수 있는 정적 메모리 할당을 사용해서 프로그램을 만들면 , 100개를 다 쓰지 않아도 100개의 크기의 메모리를 계속해서 소모하고 있다. 이렇게 프로그램이 정적 메모리 할당으로만 만들어진다면 프로그램이 복잡해지고
커질수록 메모리 공간을 많이 필요로 하는 프로그램이 될 것이다. 이런 비효율적인 프로그램을 개선하기 위해서 사용자가 원하는 만큼만 메모리를 사용하기 위한 방법이 동적 메모리 할당이다. 동적 메모리 할당을 사용함으로써 효율적이고 컴팩트한 프로그램을 만들 수 있다.
동적 메모리 할당은 시작과 종료가 필요하다.
정적 메모리 할당 같은 경우에는 컴파일러에서 미리 준비하고 알아서 처리한다.
하지만 동적 메모리 할당은 사용자가 원하는 시점에 메모리를 할당받고 원하는 시점에 할당을 해제할 수 있는 방법이기에
동적으로 메모리를 할당 받았으면 꼭 종료해줘야한다. 메모리 할당을 해제해주지 않는다면 이미 프로그램에서 메모리를 다 사용했더라도 할당받은 메모리를 계속해서 가지고 있게 된다. 이런 형상을 메모리 누수현상이라고 한다.
동적 메모리 할당과 해제 방법
malloc 함수와 free 함수를 사용한다.
int *a;
a=(int *)malloc(5*sizeof(int));
// ar 사용
free(ar);
100바이트가 필요하면 malloc(100)이라고 호출하는 식으로 쓸 수 있고, 실행 중에 할당하는 것이므로 malloc(num)과 같이 변수도 사용할 수 있다.
malloc은 응용 프로그램이 필요로 하는 양만큼 운영체제에게 할당을 요청하며 운영체제는 사용되지 않는 빈 영역(힙)을 찾아 요청한만큼 메모리를 할당하여 그 시작번지를 리턴한다.
응용 프로그램이 할당한 메모리를 어떤 목적에 사용할 지는 알 수 없으므로 malloc은 void *형을 리턴하며 받는 쪾에서는 원하는 타입으로 캐스팅해야한다.
free 함수는 동적으로 할당한 메모리를 해제한다. 다 사용한 후에 free 함수를 호출하여 메모리를 해제해야 다른 프로그램을 위해 재활용 될 수 있다.
동적으로 할당된 메모리를 사용하려면 그 시작 번지를 기억해야하므로 포인터 변수가 필요하다.
위 예의 경우 정수형 변수를 위한 메로리를 할당하는 것이므로 정수형 포인터 a를 선언했다.
malloc을 호출할 때는 필요한 메모리양을 바이트 단위로 전달하는데 정수형 변수 5개를 담으려면 5*sizeof(int)로 20바이트여야 한다. int가 항상 4바이트라는 보장이 없으므로 여기에도 반드시 sizeof 연산자로 크기를 계산해야 한다.
malloc은 인수로 전달된 크기만큼의 메모리를 할당하고 그 시작 번지를 리턴하는데, 리턴 타입이 void * 형이므로
이 포인터를 변수에 대입할 때는 반드시 원하는 타입으로 캐스팅 해야한다.
void *형 변수로 받을 수도 있는데 이렇게 하면 받을 때는 편하지만 쓸 때마다 캐스팅해야 하므로 더 불편하다. 정수형 포인터에 대입해야 하므로 (int *)로 캐스팅했다. 이렇게 할당하면 a 번지 이후 40바이트를 응용 프로그램이 배타적으로 소유하게 된다.
즉 포인터 a가 가리키는 번지는 a[5] 정수형 배열과 같고 메모리 내에서 실제 모양과 용도, 적용되는 문법도 배열과 동일하다. a 번지 이후 20바이트는 다른 목적에 사용되지 않으므로 마치 정적 할당된 배열처럼 이 메모리를 활용할 수 있다.
그리고 사용하고 난 후에는 free 함수로 메모리를 해제해야 한다.
void main()
{
int *arScore;
int i,stNum;
int sum;
printf("학생수를 입력하세요 : ");
scanf("%d",&stNum);
arScore=(int *)malloc(stNum*sizeof(int));
if (arScore == NULL)
{
printf("메모리가 부족합니다.\n");
exit(0);
}
for (i=0;i<stNum;i++)
{
printf("%d번 학생의 성적을 입력하세요 : ",i+1);
scanf("%d",&arScore[i]);
}
sum=0;
for (i=0;i<stNum;i++)
{
sum+=arScore[i];
}
printf("\n총점은 %d점이고 평균은 %d점입니다.\n", sum,sum/stNum);
free(arScore);
}
위 예에서는 학생 수가 고정되어 있지 않고 실행 직후에 입력받은 학생 수만큼 성적을 처리할 수 있는 성적 처리 프로그램이다.
실행 후 총 학생 수를 stNum에 입력받고 stNum만큼 정수를 저장할 수 있도록 arScore 배열을 할당했다.
stNum 크기의 배열을 동적으로 할당했으므로 arScore는 stNum 크기의 정수형 배열과 똑같아지며 이후 코드에서는 arScore를 정적 할당된 배열과 똑같은 방법으로 사용할 수 있다.
arScore[i] 연산식으로 i번째 요소를 자유롭게 쓸 수 있게 되는 것이다.
그리고 메모리를 할당받아 쓰다가 다 쓰고 나면 free로 해제하면 된다.
malloc 함수는 할당에 실패하면 에러의 표시로 NULL을 리턴하기 때문에 리턴한 번지를 반드시 점검하는게 원칙이다.
메모리가 부족한 상황은 언제든지 발생할 수 있고 만약 이 점검을 하지 않으면 0번지를 액세스 할 위험이 있다.
참고
https://m.blog.naver.com/cache798/130033385486