`realloc` — 메모리 영역을 재할당하는 함수
realloc — 메모리 영역을 재할당하는 함수
malloc으로 배열을 잡았는데, 나중에 보니 공간이 부족할 때가 있죠. 그럴 때 처음부터 다시 잡기보다, 기존 데이터를 유지하면서 크기만 바꾸는 게 편합니다. 그걸 하는 함수가 realloc이에요.
본문
realloc은 주어진 메모리 영역을 새 크기로 재할당해요.
문법
#include <stdlib.h>
void *realloc( void *ptr, size_t new_size );
설명
ptr이 NULL이 아니라면, 이전에 malloc, calloc 또는 realloc으로 할당됐고 아직 free나 realloc으로 해제되지 않은 값이어야 해요. 그렇지 않으면 결과는 정의되지 않습니다.
재할당은 두 가지 방식 중 하나로 이루어져요.
- (a) 가능하다면
ptr이 가리키는 기존 영역을 늘리거나 줄인다. 영역의 내용은 새 크기와 이전 크기 중 작은 쪽까지 그대로 유지되고, 늘어난 부분의 내용은 정의되지 않아요. - (b)
new_size바이트의 새 메모리 블록을 할당하고, 이전 크기와 새 크기 중 작은 쪽만큼의 메모리를 복사한 뒤 그 옛 블록을 해제한다.
메모리가 부족하면 옛 메모리 블록은 해제되지 않고 널 포인터가 돌아와요. ptr이 NULL이면 malloc(new_size)을 호출하는 것과 동일하게 동작합니다.
new_size가 0인 경우에는 표준 버전에 따라 달라집니다. C17까지는 동작이 구현에 정의됐어요(널 포인터가 돌아올 수도 있고 — 이때 옛 블록은 해제될 수도, 안 될 수도 있음 — 또는 저장소에 접근하면 안 되는 널 아닌 포인터가 돌아올 수도 있음). 이런 사용은 C DR 400을 통해 폐기(deprecated)됐고, C23부터는 동작이 정의되지 않습니다.
realloc은 스레드 안전(thread-safe)해요. 인자를 통해 보이는 메모리 위치만 접근하는 것처럼 동작하죠. 한 영역의 메모리를 해제하는 이전의 free나 realloc 호출은, 같은 영역이나 그 일부를 할당하는 어떤 할당 함수 호출과도 동기화됩니다.
매개변수
| 매개변수 | 설명 |
|---|---|
ptr |
재할당할 메모리 영역을 가리키는 포인터 |
new_size |
배열의 새 크기(바이트) |
반환값
성공하면 새로 할당된 메모리의 시작을 가리키는 포인터를 돌려줘요. 메모리 누수를 막으려면 이 포인터를 free나 realloc로 해제해야 합니다. 원래 포인터 ptr은 무효화되고, 그 포인터로의 어떤 접근도 정의되지 않아요(재할당이 제자리에서 이뤄졌더라도).
실패하면 널 포인터를 돌려주고, 원래 포인터 ptr은 그대로 유효하므로 free나 realloc로 해제해야 합니다.
주의
원래(C89에서) 0 크기 지원은 이런 코드를 수용하기 위해 추가됐어요.
OBJ *p = calloc(0, sizeof(OBJ)); // "zero-length" placeholder
/*...*/
while (1)
{
p = realloc(p, c * sizeof(OBJ)); // reallocations until size settles
/* code that may change c or break out of loop */
}
예시
malloc으로 초기 배열을 잡고 realloc을 반복하며 크기를 키우면서, 데이터가 유지되는지 확인하는 예시예요.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void print_storage_info(const int *next, const int *prev, int ints)
{
if (next)
printf("%s location: %p. Size: %d ints (%ld bytes).\n",
(next != prev ? "New" : "Old"), (void*)next, ints, ints * sizeof(int));
else
printf("Allocation failed.\n");
}
int main(void)
{
const int pattern[] = {1, 2, 3, 4, 5, 6, 7, 8};
const int pattern_size = sizeof pattern / sizeof(int);
int *next = NULL, *prev = NULL;
if ((next = (int*)malloc(pattern_size * sizeof *next))) // allocates an array
{
memcpy(next, pattern, sizeof pattern); // fills the array
print_storage_info(next, prev, pattern_size);
}
else
return EXIT_FAILURE;
// Reallocate in cycle using the following values as a new storage size.
const int realloc_size[] = {10, 12, 512, 32768, 65536, 32768};
for (int i = 0; i != sizeof realloc_size / sizeof(int); ++i)
{
if ((next = (int*)realloc(prev = next, realloc_size[i] * sizeof(int))))
{
print_storage_info(next, prev, realloc_size[i]);
assert(!memcmp(next, pattern, sizeof pattern)); // is pattern held
}
else // if realloc failed, the original pointer needs to be freed
{
free(prev);
return EXIT_FAILURE;
}
}
free(next); // finally, frees the storage
return EXIT_SUCCESS;
}
가능한 출력:
New location: 0x144c010. Size: 8 ints (32 bytes).
Old location: 0x144c010. Size: 10 ints (40 bytes).
New location: 0x144c450. Size: 12 ints (48 bytes).
Old location: 0x144c450. Size: 512 ints (2048 bytes).
Old location: 0x144c450. Size: 32768 ints (131072 bytes).
New location: 0x7f490c5bd010. Size: 65536 ints (262144 bytes).
Old location: 0x7f490c5bd010. Size: 32768 ints (131072 bytes).
"New"는 재할당으로 주소가 새로 잡혔다는 뜻이고, "Old"는 제자리에서 크기만 바뀌었다는 뜻이에요. 크기를 늘려도 pattern 데이터가 그대로 보존되는 걸 assert로 확인합니다. 그리고 realloc이 실패하면 옛 포인터를 free로 해제하는 흐름도 눈여겨보세요.
더 알아보기
malloc— 메모리를 할당해요.calloc— 0으로 초기화된 메모리를 할당해요.- cppreference의 realloc 원문에서 표준 항목별 세부 규칙을 더 확인할 수 있어요.