메모리를 동적으로 할당 받는 이유는 일반 변수는 컴파일 단계에서 변수의 크기가 정해지기 때문에 프로그램이 실행 되면서 얼마만큼의 배열크기를 사용하게 될지 정확히 알 수 없기 때문에 넉넉하게 할당 받는다. 그래서 프로그램이 실행도중에 사용자가 원하는 만큼 메모리를 할당 받을 수 있다면 메모리 낭비가 줄어들고 프로그램도 안정적으로 동작이 된다. 그래서 나온 개념이 메모리 동적 할당이다.

C언어에는 Heap 영역에 사용자가 원하는 크기만큼 메모리를 할당받아 사용할 수 있다.


그것을 지원하는 3가지 함수가 있는데 malloc, calloc, realloc 이다. 이 포스팅에서는 이 3가지 함수에 차이점에 대해서 배워보도록 하자. 기본적으로 malloc 을 많이 사용하지만 다른 함수들도 알아 두면 유용할때가 많다.


그리고 참고로 메모리를 해제하는 함수는 free이다. 메모리를 할당하고 해제하지 않으면 나중에 사용할 heap 영역에 크기가 부족해 프로그램이 정상동작하지 않을 수 있다.


우선 malloc과 free에 기본적인 사용방법은 아래 링크를 참고하자. 정확히 이해하지 않으면 이번 포스팅이 어려울 수 있다.


C/C++ malloc, free를 사용한 메모리 할당 및 해제 : http://hijuworld.tistory.com/59


그리고 메모리 할당과 해제를 하는 원리는 아래 링크를 참고하자.


C, C++ 에서 동적 메모리의 할당과 해제 원리 : http://hijuworld.tistory.com/28





malloc과 calloc의 차이에 대해서 알아보자.


1. 할당받을 메모리 사이즈를 받는 파라미터의 갯수가 다르다.

2. calloc은 메모리 할당을 받고 0으로 초기화 한다.



우선 함수의 선언을 보면 아래와 같다.


void *malloc(size_t size);

void *calloc(size_t num, size_t size);


malloc은 메모리를 할당받을 사이즈 하나만을 파라미터로 받는 반면

calloc은 메모리를 할당받을 사이즈에 그 메모리를 몇개를 할당 받을지 갯수도 파라미터로 받는다.

둘다 void 포인터를 반환하는데 그 이유는 할당한 메모리를 어떤 크기로 잘라서 사용할지는 그때그때 다르기 때문에 사용자가 강제형변환을 통해 사용하면 되기 때문이다. 


아래 간단한 예제를 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
 
int main() {        
    int *pi1; 
    int *pi2;
 
    pi1 = (int *)malloc(sizeof(int* 3); //할당
    pi1[0= 1;
    pi1[1= 5;
    pi1[2= 8;
    
    pi2 = (int *)calloc(3sizeof(int)); //할당
    pi2[0= 1;
    pi2[1= 5;
    pi2[2= 8;    
 
    free(pi1); //메모리 해제
    free(pi2); //메모리 해제
}
cs


pi1 은 malloc으로 할당을 받고 pi2 는 calloc으로 할당을 받았다. 

파라미터의 갯수 말고도 더 큰 차이가 있다.

바로 calloc은 메모리를 할당과 동시에 0으로 초기화 하는 것이다.


아래 예제를 보자. 모두 할당만 하고 값을 출력시켜 보았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
 
int main() {        
    int *pi1; 
    int *pi2;
 
    pi1 = (int *)malloc(sizeof(int* 3); //할당
    printf("malloc : %d %d %d\n", pi1[0], pi1[1], pi1[2]);
    
    pi2 = (int *)calloc(3sizeof(int)); //할당
    printf("calloc : %d %d %d\n", pi2[0], pi2[1], pi2[2]);
 
    free(pi1); //메모리 해제
    free(pi2); //메모리 해제
}
cs



출력결과

malloc : -842150451 -842150451 -842150451

calloc : 0 0 0                                          


할당하면서 0으로 초기화가 되기때문에 상황에 따라 편리할 수 있다.


realloc에대해서 알아보자.

realloc은 malloc이나 calloc으로 할당받은 메모리의 사이즈를 변경할 때 사용한다.


함수선언은 아래와 같다.

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


파라미터로 기존에 할당받았던 메모리를 전달하고 다음 파라미터로 새로 할당 받을 size를 전달한다.


아래 예제를 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
 
int main() 
{        
    int i;
    int *pi = (int *)malloc(sizeof(int* 3); //4x3 메모리 할당
    for (i = 0; i < 3; i++)
        pi[i] = i * 4;
    for (i = 0; i < 3; i++)
        printf("pi[%d] : memory %d %d\n", i, &pi[i], pi[i]);
    printf("-----------------------------\n");
 
    pi = (int *)realloc(pi,sizeof(int* 5); //4x5 메모리 재할당
    for (i = 3; i < 5; i++)
        pi[i] = i * 4;
    for (i = 0; i < 5; i++)
        printf("pi[%d] : memory %d %d\n", i, &pi[i], pi[i]);
    printf("-----------------------------\n");
 
    free(pi); //메모리 해제
}
cs


출력결과

pi[0] : memory 9457232 0  

pi[1] : memory 9457236 4  

pi[2] : memory 9457240 8  

-----------------------------  

pi[0] : memory 9457232 0  

pi[1] : memory 9457236 4  

pi[2] : memory 9457240 8  

pi[3] : memory 9457244 12 

pi[4] : memory 9457248 16 

-----------------------------  


처음 4X3 메모리를 할당받았고 그다음에는 4X5 메모리를 재할당 받았다.

재할당 받은 뒤에 기존에 할당받았던 값 이외에 값인 4번째, 5번째에만 값을 넣어준다.

재할당 받은 뒤에도 기존에 값은 정상적으로 존재하는 것을 확인할 수 있다.

단순히 메모리 사이즈만 변경하는 것이 아닌 내용물도 복사를 한다.

 

그리고 메모리의 주소값이 같은것을 볼 수 있는데 이것은 보장하지는 않는다.

완전 새로운 메모리에 값을 새로 할당받아 값을 복사할 수도 있다.


아래 예제를 보자. 메모리를 기존보다 훨씬 크게 할당한 것을 볼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
 
int main() 
{        
    int i;
    int *pi = (int *)malloc(sizeof(int* 30000); // 메모리 할당
    for (i = 0; i < 3; i++)
        pi[i] = i * 4;
    for (i = 0; i < 3; i++)
        printf("pi[%d] : memory %d %d\n",i,&pi[i],pi[i]);
    printf("-----------------------------\n");
 
    pi = (int *)realloc(pi,sizeof(int* 5000000); // 메모리 재할당
    for (i = 3; i < 5; i++)
        pi[i] = i * 4;
    for (i = 0; i < 5; i++)
        printf("pi[%d] : memory %d %d\n", i, &pi[i], pi[i]);
    printf("-----------------------------\n");
 
    free(pi); //메모리 해제
}
cs

출력결과

pi[0] : memory 17674192 0 

pi[1] : memory 17674196 4 

pi[2] : memory 17674200 8 

-----------------------------  

pi[0] : memory 21880896 0 

pi[1] : memory 21880900 4 

pi[2] : memory 21880904 8 

pi[3] : memory 21880908 12

pi[4] : memory 21880912 16

----------------------------- 


메모리의 주소값이 달라진 것을 볼 수 있다. 하지만 0, 4, 8은 잘 복사된 것을 볼 수 있다.




Posted by 꿈만은공돌
,

C언어와 C++에서는 new 연산자와 malloc 함수를 이용해서 힙(heap)영역에 동적 메모리 할당을 받고 delete 연산자와 free 함수를 이용해서 동적 메모리를 해제하게 된다.


heap 영역에 메모리를 할당하게 되면 자유메모리 블록 리스트(Free memory block list)로 관리가 된다.


새로운 메모리를 할당 할 때에는 자유메모리 블록 리스트를 참고하여 필요한 만큼의 메모리를 할당 할 수 있는 공간을 찾는다.


찾으면 자유 메모리 블록 리스트에 추가하고 할당한 해당 메모리 공간을 초기화 시킨다.


아래 그림을 보면 쉽게 이해가 될 것이다.



처음 malloc(7000); 으로 7000바이트에 메모리를 할당받는다.

가장 첫번째 A영이있는 앞부분부터 탐색을 시작하여 사용하지 않는 메모리를 찾습는다.

B구역이 사용하지 않는 메모리이지만 크기가 4k밖에 안되게 때문에 계속탐색을 하여 가장 뒷부분인 E영역에 할당을 하게 된다


그리고 free(C) 로 C블록을 메모리를 해제시킵니다. 그러면 C영역에 5000바이트의 메모리가 해제가 된다..

이때 malloc(5000) 으로 메모리를 다시 할당하게 된다.

A부터 해서 다시 빈메모리영역을 찾습니다. G영역이 9k가 비어있기 때문에 앞쪽부터 5000바이트에 메모리를 할당 한다.


이런방식으로 계속 메모리 할당과 해제를 계속 하다보면 메모리 단편화가 일어날 수 있다.

메모리를 할당과 해제를 계속하다보면 중간중간 사용하는 메모리와 사용하지 않는 메모리가 존재하게 되된다. 

아래그림처럼 사용하는 메모리와 사용하지 않는 메모리가 있는데 여기서 300바이트가 넘는 메모리를 할당요청하게 되면 할당이 실패하게 된다. 전체 사용하지 않는 메모리는 850바이트나 되지만 단편화가 일어나서 메모리 할당에 실패하게 된다.



메모리 단편화를 피하려면 메모리를 너무 자주 할당 해제하는 것을 피하는 것이 좋다.


그리고 이와 같은 메모리 할당 방식은 앞에서 부터 빈 메모리영역을 탐색하기 때문에  Heap 메모리 영역을 탐색하는데 많은 시간이 소요될 수 있다. 특히 메모리 크기가 크고 빈 메모리영역이 뒷쪽에 있다면 매번 메모리 할당에 오버헤드가 발생한다.


그리고 할당만 하고 해제를 하지 않으면 메모리가 낭비될 수 있다.


사용자가 직접 해제를 시켜줘야하기 때문에 많은 주의가 필요하다.


이런 사용자가 직접 메모리를 할당하고 해제하는 방식과 반대로 가비지 컬렉터가 사용하지 않는 메모리를 자동으로 해제하는 방식에 가비지 컬렉션이라는 방식이 존재한다.

C++ 이후에 등장한 대부분에 언어에서는 이런 가비지 컬렉션을 사용한다. 예를들면 JAVA, C#, GO 언어 들이 가비지 컬렉션을 사용한다.


C#/.NET 가비지 컬렉션의 메모리 할당 및 해제 기본 원리 : http://hijuworld.tistory.com/32


Posted by 꿈만은공돌
,