C언어/stdlib.h

[C언어] realloc (heap 메모리 크기 변경)

아무일도없었다 2023. 3. 28. 00:49

사용범위

Windows, Unix 등 모든 OS에서 사용가능한 표준 API 함수

기능

C언어 표준 함수로 이미 할당받은 동적메모리(Heap Memory)의 크기를 변경하기 위해 사용한다.

헤더

#include <stdlib.h>

※ 함수 사용시 stdlib.h 파일을 include 하지 않는다면 컴파일 시 error 발생 ※


함수

void* realloc(void* memory, size_t size);

파라미터

  • void* memory
    • 이미 할당받았던 heap memory의 주소를 입력한다.
  • size_t size
    • 변경할 heap memory의 크기를 입력한다.

반환값

성공시   사용 가능한 heap memory pointer가 반환되고 , 인자로 전달한 memory 가 free 된다.
실패시   NULL이 반환되고 인자로 전달한 memory는 그대로 유지된다.

잡학지식

realloc 함수heap memory를 재할당해 주는 기능을 제공하며 기존 heap memory에 있던 값을 복사해 준다.

 

하지만 실제로 사용해 보면 인자값으로 전달한 memory 주소값과 반환되는 주소값이 동일한 경우도 확인할 수 있다.

 

OS 및 Compiler 마다 조금씩 다르겠지만, 내부 알고리즘에 의해 heap memory 관리는 최적화를 하기 때문에 보통 인자로 전달하는 heap memory의 크기보다 작거나 같은 경우에는 동일한 주소의 memory가 반환되는 것을 확인할 수 있다.

 

그렇다고 인자로 전달한 heap memory의 크기보다 큰 size로 변경하는 경우 무조건 heap memory가 재할당되는 것은 아니다.

 

heap memory의 파편화를 최소화하기 위해 연속적인 heap memory 공간이 충분하다면 동일한 heap memory 주소를 반환하며, 그렇지 않은 경우에 인자로 전달한 heap memory를 free하고 새로운 heap memory를 재할당하여 반환하게 된다.

 

따라서 realloc 사용 시 인자값으로 넘긴 memory를 사용하지 말고 반환받는 pointer 주소를 NULL 체크한 이후에 사용해야 한다. (사용을 다 하고 난 후에는 반드시 free를 해야한다. → 인자로 전달한 memory는 free 하지 말 것)

 

또한 프로그램 내에서 realloc이 빈번하게 발생하는 경우 heap memory의 파편화로 인해 메모리 재할당에 실패하는 경우를 종종 확인할 수 있다. (malloc, calloc과 같은 함수에 비해 실패할 확률이 매우 높은 편이다.)

 

따라서 빈번한 realloc이 발생할 여지가 있는 경우 최대 heap size를 미리 계산하여 malloc 혹은 calloc으로 heap memory를 여유롭게 확보해 두고 사용하는 것을 추천한다. (realloc을 여러 번 해서 memory 파편화와 잦은 재할당을 하는 것보다는 크게 잡고 오래 사용하는 것이 성능에 좋다.)

 

참고로 필자의 경우 realloc은 정말 어쩔 수 없는 상황이 아니면 사용하지 않는다. (비교적 잦은 실패와 성능 이슈 때문)

 


 

<소스 코드>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HEAP_SIZE 32

int main() {
    char *heap_memory = NULL;
    char *realloc_memory = NULL;

    heap_memory = (char *)calloc(HEAP_SIZE, sizeof(char));
    if(heap_memory == NULL) {
        printf("calloc failed\n");
        abort();
    }

    strncpy(heap_memory, "Hello realloc", HEAP_SIZE - 1);

    printf("[before] heap_memory[%p]\n", heap_memory);
    printf("[before] %s\n", heap_memory);

    realloc_memory = realloc(heap_memory, HEAP_SIZE * 2);
    if(realloc_memory) {
        printf("[after] heap_memory[%p] realloc_memory[%p]\n", heap_memory, realloc_memory);
        printf("[after] %s\n", realloc_memory);
    } else {
        printf("realloc failed\n");
    }

    free(realloc_memory);
    heap_memory = NULL;
    realloc_memory = NULL;
    
    return 0;
}

 

※ 실행 결과

[before] heap_memory[0000000000731460]
[before] Hello realloc
[after] heap_memory[0000000000731460] realloc_memory[0000000000731460]
[after] Hello realloc

 

heap memory를 2배로 늘렸지만 동일한 pointer 주소가 반환된 것을 확인할 수 있다.

이는 연속된 heap memory 공간에 여유가 있기 때문에 동일한 주소가 반환된 것이다.

 

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HEAP_SIZE 32

int main() {
    char *heap_memory = NULL;
    char *realloc_memory = NULL;

    heap_memory = (char *)calloc(HEAP_SIZE, sizeof(char));
    if(heap_memory == NULL) {
        printf("calloc failed\n");
        abort();
    }

    strncpy(heap_memory, "Hello realloc", HEAP_SIZE - 1);

    printf("[before] heap_memory[%p]\n", heap_memory);
    printf("[before] %s\n", heap_memory);

    realloc_memory = realloc(heap_memory, HEAP_SIZE * 2000);
    if(realloc_memory) {
        printf("[after] heap_memory[%p] realloc_memory[%p]\n", heap_memory, realloc_memory);
        printf("[after] %s\n", realloc_memory);
    } else {
        printf("realloc failed\n");
    }

    free(realloc_memory);
    heap_memory = NULL;
    realloc_memory = NULL;
    
    return 0;
}

 

※ 실행 결과

[before] heap_memory[0000000000781460]
[before] Hello realloc
[after] heap_memory[0000000000781460] realloc_memory[0000000000620080]
[after] Hello realloc

 

heap memory를 2000배로 늘렸더니 다른 pointer 주소가 반환된 것을 확인할 수 있다.

이는 연속된 heap memory 공간에 여유가 없기 때문에 기존 heap memory를 free 하고 새로운 주소가 반환된 것이다.

새로운 heap memory가 할당됐지만 기존 heap memory에 있던 값이 그대로 복사된 것을 확인할 수 있다.

 


 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HEAP_SIZE 32

int main() {
    char *heap_memory = NULL;
    char *realloc_memory = NULL;

    heap_memory = (char *)calloc(HEAP_SIZE, sizeof(char));
    if(heap_memory == NULL) {
        printf("calloc failed\n");
        abort();
    }

    strncpy(heap_memory, "Hello realloc", HEAP_SIZE - 1);

    printf("[before] heap_memory[%p]\n", heap_memory);
    printf("[before] %s\n", heap_memory);

    realloc_memory = realloc(heap_memory, HEAP_SIZE / 4);
    if(realloc_memory) {
        printf("[after] heap_memory[%p] realloc_memory[%p]\n", heap_memory, realloc_memory);
        printf("[after] %s\n", realloc_memory);
    } else {
        printf("realloc failed\n");
    }

    free(realloc_memory);
    heap_memory = NULL;
    realloc_memory = NULL;
    
    return 0;
}

 

※ 실행 결과

[before] heap_memory[00000000006C1460]
[before] Hello realloc
[after] heap_memory[00000000006C1460] realloc_memory[00000000006C1460]
[after] Hello realloc

 

반대로 heap memory를 4배로 줄였는데 동일한 pointer 주소가 반환된 것을 확인할 수 있다.

크기가 줄었지만 기존 memory 영역을 사용하는데 문제가 없기 때문에 동일한 영역의 주소를 반환한 것으로 보인다.

신기한 점은 heap memory의 크기는 줄었지만 안에 있는 내용은 모두 그대로 유지되고 있는 것을 확인할 수 있다.

아마도 내부에서 heap의 크기를 줄이는 행위보다 동일한 크기의 heap을 유지하는 것이 성능면으로 유리하기 때문에 그대로 유지가 되면서 변경한 size 보다 큰 데이터의 값도 유지되는 것이 아닐까 추측해 본다.

하지만 이렇게 사용하는 것은 권장하지 않는다. (예측하기 힘들거나 애매모호한 코드는 사용하지 않는 것이 좋다.)
반응형