들어가기 전에
컴퓨터 안의 메모리는 마치 사물함과 같은 구조입니다. 우리가 사용하고자 하는 사물함의 개수를 한 번 정한 이후에는, 공간이 모자란다고 해서 주변의 사물함을 마음대로 더 사용할 수는 없습니다. 이미 다른 목적으로 사용되고 있을 수도 있기 때문이죠. 이와 같이 이미 일정한 크기의 메모리가 할당되어 있는 상황에서, 그 크기를 늘리는 일은 생각만큼 단순하지는 않습니다. 이번 강의에서는 포인터와 malloc의 개념을 응용해서, 이미 정의된 배열의 크기를 바꿔보도록 하겠습니다.
학습 목표
배열의 크기를 조정하는 코드를 작성할 수 있습니다.
핵심 단어
강의 듣기
일정한 크기의 배열이 주어졌을 때, 그 크기를 키우려면 어떻게 해야 할까요?
단순하게 현재 배열이 저장되어 있는 메모리 위치의 바로 옆에 일정 크기의 메모리를 더 덧붙이면 되겠지만, 실제로는 다른 데이터가 저장되어 있을 확률이 높습니다.
따라서 안전하게 새로운 공간에 큰 크기의 메모리를 다시 할당하고 기존 배열의 값들을 하나씩 옮겨줘야 합니다.
따라서 이런 작업은 O(n), 즉 배열의 크기 n만큼의 실행 시간이 소요될 것입니다.
이 과정을 아래 코드와 같이 나타낼 수 있습니다.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *list = malloc(3 * sizeof(int));
if (list == NULL)
{
return 1;
}
list[0] = 1;
list[1] = 2;
list[2] = 3;
int *tmp = malloc(4 * sizeof(int));
if (tmp == NULL)
{
return 1;
}
for (int i = 0; i < 3; i++)
{
tmp[i] = list[i];
}
tmp[3] = 4;
free(list);
list = tmp;
for (int i = 0; i < 4; i++)
{
printf("%i\n", list[i]);
}
free(list);
}
위와 동일한 작업을 realloc 이라는 함수를 이용해서 수행할 수도 있습니다.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *list = malloc(3 * sizeof(int));
if (list == NULL)
{
return 1;
}
list[0] = 1;
list[1] = 2;
list[2] = 3;
int *tmp = realloc(list, 4 * sizeof(int));
if (tmp == NULL)
{
return 1;
}
list = tmp;
list[3] = 4;
for (int i = 0; i < 4; i++)
{
printf("%i\n", list[i]);
}
free(list);
}
생각해보기
이미 할당된 메모리의 크기를 조절할 때 임시 메모리를 새로 할당해줘야 하는 이유는 무엇인가요?
comment
이미 할당된 메모리의 다음 위치에 빈 공간이 없을 수 있음. 그러니 임시 메모리를 새로 할당하여 공간을 확보한 후 옮기는 것이 안전함.
임시 메모리를 통해 할당된 메모리의 다음 위치에 공간이 없는 상황의 문제를 예방할 수 있습니다.
이미 할당된 메모리의 위치 옆에 빈 메모리 공간이 없을 수 있다. 따라서 새로운 임시 메모리를 할당하여 충분한 공간을 확보한 후 복사해야한다.
할당할 메모리에 다른 값이 있을 수 있기 때문
-> 다른 값이 있을 경우 새로 할당할 값과 충돌이 일어남
메모리의 크기를 동적으로 조절하는 것은 메모리 관리 측면에서 효율성과 안정성을 유지하기 위해서이다. 여러 이류로 인해 이미 할당된 메모리의 크기를 직접 조절하는 것이 어려울 수 있다.
1. 연속된 메모리 요구: 메모리는 주로 연속된 블록으로 할당이 됨. 따라서 메모리를 늘리거나 줄일 때, 새로 할당된 메모리 공간이 연속되어 있어야 함. 이미 할당된 메모리 옆에 충분한 연속된 공간이 없다면, 크기를 직접 조절하기 어려움,
2. 데이터 이동의 효율성: 기존 메모리의 크기를 조절할 때, 메모리의 끝이나 시작 부분에 여유 공간이 있더라도 데이터를 이동하는 과정에서 문제가 발생할 수 있음. 새로운 메모리 공간에 데이터를 복사하는 것이 안전하고 효율적인 방법일 수 있음.
3. 메모리 누수 방지: 새로운 메모리를 할당하고 기존 데이터를 복사한 후, 이전에 사용한 메모리를 명시적으로 해제함으로싸 메모리 누수를 방지할 수 있음. 직접 크기를 조절하는 경우 실수로 메모리를 해제하지 않거나 잘못된 위치를 해제할 수 있음
이러한 이유로, 동적 메모리 조절은 안전성과 효율성을 유지하기 위해 새로운 메모리를 할당하고 데이터를 옮기는 방식을 선호함.
위 내용은 ChatGPT의 내용을 참고함.
1.연속된 메모리 공간의 제한: 메모리 할당은 보통 연속된 메모리 공간에 이루어집니다. 따라서, 이미 할당된 메모리 영역을 크기를 조절하기 위해서는 해당 메모리 영역 뒤에 연속된 비할당 공간이 있어야 합니다. 만약 이미 할당된 메모리 영역 뒤에 연속된 비할당 공간이 부족하거나 없다면, 크기를 조절하기 위해서는 새로운 메모리 영역을 할당하고 데이터를 복사해야 합니다.
2.메모리 재사용의 용이성: 이미 할당된 메모리 영역을 크기를 조절하기 위해 임시 메모리를 할당하고 데이터를 복사한 뒤, 원래의 메모리 영역을 해제할 수 있습니다. 이를 통해 메모리의 재사용을 용이하게 할 수 있습니다. 만약 크기를 조절하는 과정에서 원래의 메모리 영역을 사용하면, 임시 메모리를 사용한 동안 해당 메모리 영역을 다른 용도로 사용할 수 없으므로 메모리 사용 효율이 떨어질 수 있습니다.
3.데이터 일관성 유지: 메모리 크기를 조절하는 과정에서 임시 메모리를 사용하여 데이터를 복사하는 것은 데이터의 일관성을 유지하기 위함입니다. 데이터를 새로운 메모리 영역으로 복사한 뒤, 원래의 메모리 영역을 해제함으로써, 데이터의 원본과 복사본 사이에 일관성을 유지할 수 있습니다. 이는 예기치 않은 동작이나 데이터 손실을 방지하기 위해 중요합니다.
이미 할당된 메모리 밖에는 이미 다른 데이터가 있기 떄문이다.
임시 메모리 변수를 할당하지 않고 기존의 메모리 변수에 ex) list = malloc(4*sizeof(int)); 이렇게 재할당을 하게 되면 기존의
메모리들이 가리키는 주소값이 사라지는 오류가 생깁니다.
기존 메모리를 가리키던 변수에 새로 할당하게 되면, 기존 점유하고 있던 메모리를 가리키는 주소값을 모르게 돼서 이를 이용하거나 해제할 수 없게 됩니다.
할당된 메모리 다음에 다른 값이 저장되어있어 메모리를 연속적으로 사용할 수 없기 때문에 조절하고자 하는 크기로 임시 메모리를 새로 할당하여 기존의 값을 그대로 옮겨주고 추가로 할당한 메모리에 새로운 값을 추가 할 수 있다.
만약 임시 메모리 없이 이미 할당된 메모리가 있는 포인터에 다른 주소를 넣는다면, 기존 정보가 사라지게 되기 때문이다. 이는 메모리는 여전히 남아있지만 접근 방법은 존재하지 않는 다는 점에서 문제다.
처음 할당된 메모리 영역 앞뒤로 이미 할당된 영역이 있을 가능성이 있으므로,
연속적인 메모리 할당을 보장할 수 없기 때문입니다.
이미 할당된 메모리의 크기를 조절할 때 임시 메모리를 새로 할당해줘야 하는 이유는 데이터는 연속적으로 저장되기 때문에 할당된 메모리 다음 공간에는 알 수 없는 값이 들어있을 수 있습니다. 따라서 다음 공간의 알 수 없는 값에 영향을 주지 않기 위해서 메모리를 새로 할당해야 합니다.
메모리의 관점으로 보면 확장시에 어떤 메모리 데이터 값에 영향을 줄지 모르며 기존 주소에서 확장을 시도할 때 많은 메모리 이동이 발생할 수 있기 때문에 메모리를 새로 할당 해줘야 합니다.
C언어에는 이미 할당된 메모리 블록의 크기를 직접 변경하는 기능은 없다. 따라서 새로운 크기의 메모리 블록을 할당하고, 기존 데이터를 복사한 다음 원래 메모리 블록을 해제하는 방식을 사용한다
이와 같은 방식으로 메모리의 크기를 조절하기 때문에 임시 메모리를 새로 할당해주는 것이다.
그 전 메모리를 가리키고 있는 포인터가 사라져서 다시는 찾을 수 없게됩니다. 다시 찾을 수 없는 메모리는 해제 할 수 없으므로 메모리 누수로 이어지게 됩니다.
크기를 키우려는 공간에 다른 데이터가 저장되어 있을 수 있기 때문이다
할당된 메모리 주변에 저장된 값이 있을 수 있어서 그 주변 메모리로 확장시켜 메모리를 할당하면 본래 저장된 값이 cover되어 사라질 수 있다. 이를 방지하기 위해 임시 메모리를 새로 할당해 주어야 한다.
할당된 메모리 바로 옆에 다른 데이터가 저장되어 있을 수 있기 때문에 원하는 크기의 임시메모리를 할당하고 기존값을 복사한 뒤 기존값을 해제 해주어야 한다.
이미 할당된 메모리는 다른 주소가 이미 정해져있어서 새로 할당해야된다.