동적 메모리 할당은 사용자가 프로그램이 실행도중 원하는 메모리의 크기를 할당하여 사용하는 것이다.

프로그래머가 코드를 작성할 당시에 정확히 얼마에 메모리가 필요한지 알 수 없기때문에 프로그램 실행도중 필요한 만큼만 할당받기 때문에 메모리 낭비를 줄일수 있다.

포인터가 사용되기 때문에 포인터에 대한 이해가 낮으면 조금 어려울 수 있는 내용이다. 그래도 계속 보다보면 적응이 될 것이다.


C언어에서는 힙영역에 사용자가 메모리를 동적 할당하는 3가지 함수인 malloc, calloc, realloc 이 있고

메모리를 해제하는 함수인 free가 있다.


메모리 할당 및 해제에 관한 원리는 아래 링크를 참고하자.

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


동적 할당하는 3가지 함수인 malloc, calloc, realloc 의 차이에 대해서는 아래 링크를 참고하자.

C/C++ 동적 메모리할당 malloc, calloc, realloc 함수 비교 및 예제 : http://hijuworld.tistory.com/60





가장 기본인 동적 할당 함수인 malloc에 대해서 알아보자.


#include <stdlib.h> 

void* malloc(size_t size);


선언은 위와 같은 형식이다.

파라미터로 할당할 메모리의 크기를 넘기고 반환형이 void 형 포인터이다.

void 포인터인 이유는 할당받은 메모리를 몇바이트씩 잘라서 사용할지는 그때그때 다르기 때문에 사용자가 형변환을 통해서 사용하면 되기 때문이다. 


간단하게 예제를 보자. 1바이트 메모리를 할당해서 char형 포인터에서 할당한 주소값을 저장한다.

1바이트할당 받은 것을 (char*) 를 사용해서 강제형변환을 한 것을 볼 수 있다. 


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
 
int main() {        
    char *= (char*)malloc(1); //1바이트 메모리 할당
    *= 'h';
    printf("%d, %c\n",c, *c);//c의 주소값, c의 값 'h'
    free(c); //메모리 해제
}
cs

출력 결과

17674200, h


 할당받은 것을 그림으로 그려보면 아래와 같다. 우선 malloc을 이용해서 힙영역에 0x1000번지에 1바이트에 메모리를 할당 한다. 그리고 스택영역에 선언한 지역변수인 포인터 C가 malloc으로 할당한 주소를 가지고 있는다. 

그리고 해당 포인터를 가지고 *c = 'h'로 malloc으로 할당한 메모리에 문자열을 저장한다.



 파라미터로 사이즈를 넘기기 때문에 위의 예제처럼 직접 숫자를 입력해도 가능하진 하지만 아래 예제와 같이 sizeof 를 써서 할당할 변수의 크기와 그 변수의 갯수를 입력하는것이 휴먼에러를 줄이는 방법이다. char 가 1바이트고 int가 4바이트고 float이 4바이트 라는 보장은 할 수 없다. 컴파일러나 운영체제에 따라서 달라질 수 있기 때문이다. 그래서 가장 안전한 방법인 sizeof() 함수를 이용해서 해당 변수에 크기를 그때그때 검사해주는 방법이 좋다. 


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
 
int main() {        
    char *= (char*)malloc(sizeof(char)); //char 변수 하나의 메모리 할당
    *= 'h';
    printf("%d, %c\n",c, *c);//c의 주소값, c의 값 'h'
    free(c); //메모리 해제
}
cs

비슷한 예제로 int형 변수 4개를 할당받아보자. int에 크기에 4개를 곱한 사이즈를 할당 받았다.

그리고 (int *)로 형변환을 시켜서 i[1] 등을 했을때 4바이트씩 메모리를 증가시키게 된다. 그래서 아래와 같이 배열처럼 사용을 하면 된다. i[1] 은 *(i+1)과 완벽하게 같은 말이다. 


1
2
3
4
5
6
7
8
9
int main() {        
    int *= (int*)malloc(sizeof(int* 4); //int 사이즈 4개 메모리 할당
    i[0= 5;
    i[1= 1;
    i[2= 10;
    i[3= 20;
    printf("%d %d %d %d\n",i[0],i[1],i[2],i[3]);
    free(i); //메모리 해제
}
cs


출력 결과

5 1 10 20


sizeof(int) * 4를 그림으로 그려보면 아래와 같다.  힙영역에 0x1000번지 부터 총 16바이트에 메모리를 할당 받았다. 그리고 지역변수로 선언산 int형 포인터 변수인 i가 그 시작 주소값을 저장한다. i는 int* 이기 때문에 i는 0x1000번지를 가리키고 i+1을 하면 4바이트가 증가하여 1004번지를 가리키게 된다. 그래서 배열과 같이 사용할 수 있게 된다.


2차원 배열을 메모리로 할당 받아보자.

더블포인터를 이용해서 한번 할당받고 다시 각각의 2차원배열 요소들을 할당 받아야 한다.

아래예제를 보자. 처음보면 상당히 복잡하고 난해할 수 있다. 특히 이중포인터를 사용했기 때문에 쉽게 이해가 가지 않을 수 있다. 이해가 가지 않는다면 우선 해당 코드를 복사해서 사용하고 차차 이해해도 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main() {        
    int **ppi = (int**)malloc(sizeof(int** 3); //int* 사이즈 3개 메모리 할당
    int i,j;
    for(i = 0; i < 3; i++)
        ppi[i] = (int*)malloc(sizeof(int* 2); //int 사이즈 2개 메모리 할당
    
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 2; j++) {
            ppi[i][j] = (i + 1) * (j + 1);
        }
    }
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 2; j++) {
            printf("%d ", ppi[i][j]);
        }
    }
    printf("\n");
    
    for (i = 0; i < 3; i++)
        free(ppi[i]); //메모리 해제
    free(ppi); //메모리 해제
}
cs


출력결과

1 2 2 4 3 6


그리고 메모리 해제역시 2차원배열이기 때문에 각 요소들을 해제하고 2차원배열을 해제해줘야 한다.



Posted by 꿈만은공돌
,