티스토리 뷰
주의 사항!
- 이 일지는 작성하고 있는 현시점, 공부와 병행하면서 작성되고 있습니다.
- 공부 중에 떠오른 생각이나 그때그때의 개념정리 같은 내용이 포함됩니다.
- 따라서 이 일지의 내용은 제가 공부하고 이해한 대로 작성되기 때문에 실제 사실과는 다를 수 있습니다
이번 시간은 메모리 동적 할당에 관한 내용입니다.
프로그램을 작성할 땐 프로그램 실행 중 처리하게 될 데이터 종류나 수를 확실히 알 수 없는 경우가 생깁니다.
메모리 처리 방식을 직접적으로 작성하는 C언어에서는 이런 경우가 프로그래밍에 꽤 치명적일 수 있다고 생각합니다.
하지만 대처방법이 있습니다.
프로그램이 실행되는 도중에 필요한 메모리 공간을 동적으로 확보하는 것이 그것입니다.
이번 시간에는 메모리 공간을 동적으로 할당하는 함수의 사용법과 할당된 저장 공간을 활용하는 방법을 살펴봅니다.
동적할당과 관련한 함수로 4가지 정도가 소개되어 있습니다.
void* malloc (unsigned int size);
void* calloc (unsigned int count, unsigned int size);
void* realloc (void* pointer, unsigned int size);
void free (void* pointer);
malloc 함수는 size 바이트 수 만큼 할당하고 시작 위치를 반환합니다.
calloc 함수는 (count * size) 바이트 수 만큼 할당하고 0으로 초기화 후 시작 위치를 반환합니다.
realoc 함수는 pointer가 연결한 영역의 크기를 size 바이트의 크기로 조정하고 시작 위치를 반환합니다.
free 함수는 pointer가 연결한 영역을 반환합니다. 즉, 동적 할당된 공간을 재사용을 위해 다시 반환할 때 사용합니다.
지금 당장 다 이해할 필요는 없습니다.
그런데 malloc, calloc, realloc 3개 함수에 특이한 부분이 눈에 띕니다. 반환형이 void* 로 선언되어 있습니다.
이전에 void*는 void 포인터를 선언할 때 사용했었습니다.
void 포인터는 이 포인터에 어떤 데이터의 메모리 주소를 저장하고 싶은데 그 데이터의 자료형을 알 수 없거나
확정할 수 없는 경우에 void* 를 이용해서 사용했었습니다.
여기서 함수의 반환형이 void*인 이유를 유추해 볼 수 있을 것 같습니다.
함수는 포인터의 형태로 시작위치를 반환합니다. 그 시작위치는 메모리의 첫번째 요소의 주소일 것이고,
이 주소를 반환하려는데 데이터의 자료형은 쓰기에 따라 달라질 수 있으니 void*로 선언한 것으로 생각됩니다.
위 함수들을 사용할 때는 stdlib.h 헤더 파일을 인클루드해야 합니다.
간단한 예시를 통해 malloc 함수와 free 함수의 기능과 사용법을 살펴보겠습니다.
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
int* pInt;
double* pDouble;
pInt = (int*)malloc(sizeof(int));
if (pInt == NULL)
{
printf("#으로 메모리가 부족합니다.");
exit(1);
}
pDouble = (double*)malloc(sizeof(double));
if (pDouble == NULL)
{
printf("#으로 메모리가 부족합니다.");
exit(1);
}
*pInt = 10;
*pDouble = 3.5;
printf("정수형으로 사용 : %d\n", *pInt);
printf("실수형으로 사용 : %.1lf\n", *pDouble);
free(pInt);
free(pDouble);
}
/*
실행결과
정수형으로 사용 : 10
실수형으로 사용 : 3.5
*/
int형 포인터와 double형 포인터를 선언했습니다.
먼저 malloc 함수를 이용해 메모리에 공간을 동적으로 할당하고 그 시작주소를 pInt에 저장합니다.
malloc함수는 반환형이 void* 이기 때문에 자료형을 (int*) 과 같이 지정해주어야 합니다.
malloc 함수는 매개변수로 할당할 메모리 공간의 크기를 받습니다.
int형은 보통 4바이트 double형은 보통 8바이트라는 사실을 알지만, 컴파일러에 따라서 이 크기는 바뀔 수 있기 때문에
sizeof() 를 이용해서 원하는 자료형의 크기를 대입시켜 주는 것이 좋습니다.
malloc 함수는 메모리에 동적으로 할당할 공간이 충분하지 않으면 NULL 포인터를 반환합니다.
그래서 만약 malloc 함수가 반환한 값이 NULL값인지 확인하고, 그러하다면 exit() 에 매개변수로 1을 주어 호출합니다.
exit(1) 을 호출하게 되면 프로그램을 바로 종료합니다.
malloc함수가 반환하는 값을 이렇게 확인하는 것은 매우 중요합니다.
만약 반환값을 확인하지 않았고, 하필 반환값이 NULL이었다면 프로그램은 비정상적으로 종료됩니다.
이는 malloc 뿐만 아니라 동적으로 메모리 공간을 할당하는 모든 함수들에 대해 동일하게 적용됩니다.
동적 할당된 공간이 쓰임새를 다 하면 free() 함수를 호출해 이 공간을 직접 반환해주어야 합니다.
일반적으로 선언되는 변수나 배열 등은 사용이 끝나면 그 메모리 공간을 알아서 회수해주지만
동적으로 할당한 공간은 자연적으로 회수되지 않아 직접 회수해주어야 합니다.
만약 이를 제대로 회수하지 않는다면 메모리의 공간을 그만큼 잃게 되는 것이고,
프로그램 실행 중에 이런 일이 반복적으로 일어난다면 더 이상 쓸 수 있는 메모리 공간이 남지 않아
프로그램이 비정상적으로 종료됩니다.
프로그램이 종료되면 동적으로 할당한 메모리 공간도 다시 회수됩니다.
malloc함수는 매개변수로 size만을 받았습니다. 이 size는 이 공간이 저장할 변수의 자료형의 크기입니다.
따라서 malloc 함수는 변수 하나만을 저장할 수 있는 공간을 할당합니다.
그렇다면 size에 원하는 개수만큼을 곱해서 주면 배열처럼 공간을 할당할 수 있는 걸까요?
예시를 통해 살펴보겠습니다.
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
int* pInt;
int sum = 0;
pInt = (int*)malloc(sizeof(int)*5);
if (pInt == NULL)
{
printf("메모리 공간이 부족합니다.\n");
exit(1);
}
printf("다섯명의 나이를 입력하세요 : ");
for (int i = 0; i < 5; i++)
{
scanf("%d", &pInt[i]);
sum += pInt[i];
}
printf("다섯 명의 평균 나이 : %.1lf\n", sum / 5.0);
free(pInt);
}
/*
실행결과
다섯 명의 나이를 입력하세요 : 21 27 24 22 35
다섯 명의 평균 나이 : 25.8
*/
이번에는 malloc 함수의 인수로 sizeof(int)에 5를 곱해서 주었습니다.
즉 4바이트 공간 5개를 연속으로 할당하겠다는 의미입니다.
이후 반복문을 통해 이를 배열처럼 사용할 수 있습니다.
메모리를 동적 할당할 때 malloc 함수를 가장 보편적으로 사용하지만
경우에 따라서 유용하게 쓰이는 다른 함수들도 있습니다.
calloc 함수는 메모리 공간을 동적 할당함과 동시에 그 안의 데이터를 모두 0으로 초기화합니다.
realloc 함수는 동적 할당한 공간을 더 키우거나 줄일 때 사용합니다.
간단한 예제를 통해 살펴보겠습니다.
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
int* pInt;
int size = 5;
int count = 0;
int number;
pInt = (int*)calloc(size, sizeof(int));
while (1)
{
printf("양수를 입력하세요 : ");
scanf("%d", &number);
if (number <= 0) break;
if (count == size)
{
size += 5;
pInt = (int*)realloc(pInt, size * sizeof(int));
}
pInt[count] = number;
count++;
}
for (int i = 0; i < count; i++)
{
printf("%5d", pInt[i]);
}
free(pInt);
}
/*
실행결과
양수를 입력하세요 : 1
양수를 입력하세요 : 2
양수를 입력하세요 : 3
양수를 입력하세요 : 4
양수를 입력하세요 : 5
양수를 입력하세요 : 6
양수를 입력하세요 : 7
양수를 입력하세요 : 8
양수를 입력하세요 : 9
양수를 입력하세요 : 10
양수를 입력하세요 : 11
양수를 입력하세요 : 12
양수를 입력하세요 : 0
1 2 3 4 5 6 7 8 9 10 11 12
*/
처음 calloc 함수를 사용해 4바이트 크기의 공간 5개를 동적 할당하고 데이터를 모두 0으로 초기화했습니다.
이후 반복문에서 데이터를 입력하는 중 입력할 데이터의 양이 5개를 넘어가게 됩니다.
할당된 공간까지 데이터가 가득 차게 되면 realloc 함수를 이용해 동적 할당된 공간의 크기를 5개 더 늘립니다.
realloc 함수를 사용해서 메모리 크기를 수정하게 되면 기존에 저장되어 있던 데이터는 어떻게 될까요?
기존에 입력된 데이터는 계속 유지됩니다.
만약 크기를 줄이는 경우라면 잘리는 부분의 데이터는 손실되겠지만, 반대로 크기를 늘리는 경우라면
기존의 데이터는 모두 유지되고 추가된 공간에는 쓰레기데이터가 남아있는 상태가 됩니다.
realloc 함수는 추가하는 메모리 공간에 대해서는 초기화를 수행하지 않기 때문에 쓰레기데이터가 남습니다.
realloc 함수가 메모리 크기를 수정하고 반환하는 시작주소는 다시 받지 않아도 보통은 같은 주소로 유지되지만
간혹 realloc 함수가 반환하는 주소가 기존의 주소와 다르게 바뀌는 경우가 있습니다.
기존의 메모리의 크기를 늘리는 경우 추가해야할 공간은 기존에 할당된 공간의 뒤부터 하나씩 할당됩니다.
그런데 메모리 공간을 추가로 할당하려는데 그 공간은 이미 다른 변수가 사용하고 있다면
메모리 공간을 추가로 할당할 수가 없기 때문에 자리가 넓은 다른 곳으로 기존의 공간까지 통째로 옮기게 됩니다.
기존에 할당된 부분까지 같이 옮기기 때문에 데이터 손실은 걱정하지 않아도 됩니다.
따라서 이와 같은 경우가 발생할 수 있으므로 realloc에서 반환하는 주소도 꼭 다시 저장할 필요가 있습니다.
다음은 '동적 할당 저장 공간의 활용'에 대해 배워보겠습니다.
읽어주셔서 감사합니다.
'공부 일지 > C언어 공부 일지' 카테고리의 다른 글
사용자 정의 자료형 (0) | 2021.01.29 |
---|---|
동적 메모리 할당 및 활용 관련 문제2 (0) | 2021.01.28 |
동적 메모리 할당 및 활용 관련 문제1 (0) | 2021.01.28 |
동적 할당 저장 공간의 활용 (0) | 2021.01.28 |
C언어 과정 기록에 앞서.. (0) | 2021.01.27 |