본문 바로가기

프로그래밍./C언어.

동적 메모리 할당 (malloc / calloc / realloc / free)


동적 메모리 할당 (malloc / calloc / realloc / free)

동적 메모리. malloc, calloc, realloc 에 대해서 알아보자.

일단 기본적인 개념부터 이해 하자면.

배열을 예로 들어서, 배열이 필요하지만 몇개가 필요한지 알 수 없는 경우.

우리는 일반적으로 예제를 하거나 간단한 코딩을 하는 경우에는 아래와 같이 사용한다.

int arr[100]; 100정도. 혹는 1000 실행에도 크게 문제가 없고 별 다른 문제는 없어 보인다.

하지만 만약에. 이 중 단 10개만 사용한다면? 혹은 그래서 10개만 선언 했는데 20개가 필요하게 된다면?

크게 잡아 놓으면 낭비가 되고, 모자라면 처리를 못하는 것이다.

게다가 공간 할당은 메인의 최 상위 처음 프로그램 실행시 미리 할당을 받아야 하는게 문제다.


이를 해결하기 위해서 동적인 메모리가 나왔다.

내가 이해한 기본적인 특징.

1. 처음에 미리 할당 받지 않고 프로그램 도중에 할당 받을 수 있다.

간단한 예를 들자면 학생들의 성적을 입력 받는 프로그램 작성시.
학생의 인원수는 100 또는 1000처럼 미리 지정을 해줘야 한다. 배열 또는 구조체의 크기 지정.

하지만 동적 메모리를 할당하면. 내가 입력할 학생의 수를 입력하고, 그 후에 그 인원수 만큼의 크기 할당이 가능한다.

malloc의 기본 사용법은 아래와 같다.
int *mal;
mal = (int *) malloc (sizeof(int) * size);

우선 mal 이라는 포인터를 하나 만든다. 이는 메모리를 할당 받고 그 메모리에 위치를 가리키기 위함이다.
할당 받은 곳은 어딘지 나오지 않기 때문에 할당 받음과 동시에 그곳의 위치를 표시해 둔다고 생각하면 될 듯.

당연한 이야기겠지만 size는 미리 값을 넣어주거나 혹은 size대신 숫자를 입력해주면 된다.
배열의 크기라고 생각하면 될 것이다. 자기가 원하는 크기. sizeof(int)란 타입의 크기를 반환해 주는 함수인데.
int는 4바이트 기 때문에 4가 반환된다. 기타 char이나 다른 것도 마찬가지로 그 크기 만큼 반환이 된다.

때문에 size를 5라고 가정하면 mallo(20) 이 되는 셈이다. 앞에 (int *)를 통해서 int 크기만큼의 단위라는 의미겠지.
malloc(20) 이라고 써도 앞에 int가 있기 때문에 4씩 나누어 알아서 5개가 될 것이다.

프로그램은 처음부터 할당 하는 것이 아니라 malloc 함수를 만나면 그 때 할당을 하는 것이다.

위치를 기록해둘 포인터만 미리 선언해 두고 메모리는 나중에 할당 받을 수 있다는 것.
즉, 배열을 나중에 선언할 수 있다는 의미와 비슷하다. 위의 예시 처럼 하자면

int *mal; 을 선언하고 scanf로 size의 값을 입력을 받고, malloc 함수를 사용해서 그제서야 할당을 받을 수 있다는 것.

사실 이 부분은 아직 크게 효율적이지 않아 보일 수도 있다;

여기서 실제 값을 넣는 방법에 혼돈이 있을 수 있다. mal에 위와 같이 5개를 선언했는데 변수는 포인터.
그렇다면 어떤식으로 값을 넣어야 할까? 포인터와 배열의 관계를 잘 알고 있는 사람이라면 무난히 넘어갈 것이다.
*(mal+0) = 10; 이렇게 사용하면 mal의 첫 번째에 10이 대입되는 것이다. 하지만 좀 보기 껄끄럽지 않은가?

사실 배열과 포인터의 관계에서. *(mal+1) 은 mal[1] 과 같다. 시간이 된다면 이 부분에 대에서도 글을 쓰겠지만
간단히 설명을 하자면. mal은 사실상 주소이다. int형이라 4바이트씩 건너 뛴다. 즉 mal의 첫 주소에서 +1 만큼.
int의 크기 만큼 1번 이동한 것. 배열로 따지자면 2번째 요소가 되는 것이다. 거기에 *가 붙어서 그 위치의 값이 된다.
단, 여러개가 아닌 단일의 동적 메모리를 할당 받았다면 포인터 이기 때문에 당연히 *mal = 10; 이라고 해야 한다.
메모리가 어쩌구 저쩌구 하지만 사실상 가지고 있는 변수는 포인터 라는 점을 잊지 말자.
나중에 값을 비교하는데 mal만 쓰거나 하는 어처구니 없는 실수는 하지 않도록 해야한다.



2. 할당된 메모리의 크기를 늘릴 수 있다.
정적 메모리로 할당을 10개로 받았는데 10개가 넘는다면 어찌 할 것인가?
여기서 동적메모리 더욱 빛을 낸다. realloc의 사용이다.

많이 쓸 것도 아닌데. 100, 1000으로  할당 받는게 너무 낭비라서 10개만 잡았다.
헌데 10개가 넘어버리면 낭패. 이런 경우 malloc으로 동적메모리를 할당 받는다.

그 후 10개가 넘는 상황이 발생한다면 realloc으로 메모리의 크기를 늘려주면 된다.
위와 동일하게 동적 메모리를 할당 받았다고 가정하면,

mal = realloc(mal, sizeof(int)*7); 이렇게 7개로 변경이 가능하다.
mal의 주소에 있는 메모리의 크기를 7개로 늘리는 것.

위에서 처럼 학생 성적 입력 프로그램이라 하면, 적당한 사이즈의 메모리를 할당 받고
학생을 입력 받을 때 마다 하나씩 카운트를 해주면서 실행하다가. 카운트가 메모리의 크기와 같아지거나 한다면

if문 등을 이용해서 realloc을 사용해 메모리를 늘려주는 것이다. 이처럼 사용한다면 100, 1000처럼 처음부터
크게 잡아줘서 낭비가 되는 일도, 너무 작게 잡아서 처리하지 못하는 일도 발생하지 않을 것이다.


마지막으로 calloc이 있는데 malloc과 같지만 모두 0으로 초기화 한 상태로 할당한다.
어떻게 보면 편하고 좋을지도.. 단 사용법이 조금 다른데. 크기가 아닌 항목으로 구분을 한다.
따라서 calloc(5, sizeof(int)) 이런식으로 사용한다.

malloc은 사실상 mallc(size_t size) 여기서 size는 할당받고 싶은 바이크의 크기이다.
때문에 sizeof(int) * 5 를 하던, 5 * sizeof(int)를 사용하던 값은 20이 나오기 때문에 어떤 것이는 상관 없다.

하지만 calloc의 경우는 calloc(size_t n, size_t size) 이며, n은 항목의 개수 이고 size는 항목 하나의 크기이다.

따라서 입력해야 하는 자리가 정해져 있기 때문에 위치가 바뀌어선 안된다. calloc(sizeof(int), 5)는 잘못 된 것이다.



또, 굉장히 중요한 부분이 있는데 바로 free 이다.
동적 메모리 사용 시. 사용 후에는 반드시 반환을 해주어야 한다. 이를 free라고 한다.

free에 통해서 할당 받은 메모리의 주소를 알려 주고 모두 회수 해야한다.

free(mal);




추가적으로(?) 몇가지가 있는데.

첫 번째. 위에서 설명했듯 sizeof(int)*size 의 경우 단순히 크기를 측정하기 위함이다.
때문에 구제체의 경우도 마찬가지로, (struct 구조체명 *) malloc(sizeof(struct 구조체명) * size) 를 사용하면 된다.
그리고 1개의 동적 메모리를 할당하는 경우가 있을지 잘 모르겠지만, 이런경우 *1 도 가능하겠지만
 * 개수를 쓰지 않으면 1개만 할당이 된다.

두 번째. 2차원의 선언.
이는 한번에 선언이 안되는 듯 싶다. 알다 싶이 2차원 배열은 고층짜리 아파트라고 생각하면 된다.

우선. 몇층짜리 아파트를 지을 것인지 허락을 받는다.
num =(int **) malloc(sizeof(int*)* row);  2차원이기 때문에 맨 앞에 int 에 * 하나가 더 붙고 뒤에 int에도 하나가 붙는다.

그 후 한 층에 몇 가구가 살지 허락을 받는다.
 for(i=0; i<row; i++)   당연히 아파트의 높이 만큼 반복.
  num[i]=(int *) malloc(sizeof(int)*size);   1층에 몇개의 가구가 살지, 2층에는 몇개의 가구가 살지 할당.

*의 개수에 주의하여 사용하자.




유용한 정보가 되셨다면 아래 손가락 버튼 한번 눌러주세요 ^-^