easy software

C언어 메모리 동적 할당 함수 malloc과 free를 알아보자 본문

C언어/메모리 관리와 동적 할당

C언어 메모리 동적 할당 함수 malloc과 free를 알아보자

JAVA 웹개발자 2015. 2. 13. 16:11

메모리 동적 할당이란?

 

 

프로그래머가 힙 영역에 메모리를 할당하는 것을 말합니다.

 

조금 더 구체적으로 표현하자면, 컴파일 시에 메모리를 할당 받는게 아니라

 

런타임 시(프로그램 실행 도중)에 메모리를 할당 받는 것을 뜻 합니다.

 

왜 동적 할당을 사용할까요? 예를 들어, 사용자로부터 정수를 입력받아서

 

그 정수 값만큼의 배열길이를 정하고 싶습니다.

 

그런데 배열은 상수로만 선언할 수 있는거 아시죠?

 

컴파일러가 처음에 배열을 보고 char array[i];

 

와 같이 선언되어있으면, 얼마만큼의 메모리공간을 확보해야 할지 모르기 때문에 그렇습니다.

 

그런데 저 배열의 크기를 정할 때 컴파일 시가 아닌, 런타임 시에 정해주면 되지 않을까요?

 

이러한 이유 때문에 동적 할당을 사용하는 것입니다.

 

 

 

 

malloc 함수

 

 

#include <stdlib.h>

 

void* malloc(size_t size)

 

로 선언되어있으며, 성공 시 할당된 메모리의 첫 번째 주소를 리턴합니다.

 

실패시에는 NULL 포인터를 리턴합니다.

 

매개변수로는 바이트 단위의 해당하는 정수를 전달하면 됩니다.

 

만약, 4를 전달하면 4바이트의 메모리를 할당합니다.

 

그런데 반환형이 void 포인터입니다. 왜 그런지 알아보겠습니다.

 

매개변수로 4를 전달했다고 가정하겠습니다.

 

그러면 컴파일러는 char형 배열 네개를 전달한건지? int형 정수를 전달한건지?

 

아니면 short형 두개를 전달한건지? 알 수가 없습니다.

 

그렇기 때문에 프로그래머가 알아서 형변환 하여 사용하게끔 void 포인터로 리턴하는 것입니다.

 

 

 

 

malloc 함수의 호출 형태

 

 

int * i = (int*)malloc(sizeof(int));

 

위와 같이 호출하는 것이 공식화 되어 있습니다.

 

하나씩 풀어보면, sizeof(int)는 4를 뜻합니다. 즉 4바이트 공간을 확보해야 한다는 뜻입니다.

 

그리고 (int*)으로 형변환 해주고 있습니다. 반환된 주소값을 i가 가리키도록 하고 있구요.

 

즉. '힙 영역에 4바이트 공간을 확보하되,

 

int* 형으로 형변환해서 i가 가리키도록 해라' 란 뜻입니다.

 

만약 길이7의 배열공간을 확보해야 한다면, int * i = (int*)malloc(sizeof(int) * 7);

 

로 호출하면 됩니다.

 

 

 

 

free 함수

 

 

#include <stdlib.h>

 

void free(void* ptr)

 

로 선언되어 있습니다. 매개변수로 'malloc 함수의 호출 형태' 에서 선언한

 

i를 전달하면, i 가 가리키고 있는 해당 힙 영역 메모리가 소멸됩니다.

 

솔직히, 프로그램이 종료되면 모든 리소스(메모리 등 각 종 자원)는 반환됩니다.

 

그런데 왜 free함수를 호출할까요?

 

만약 free함수를 호출하지않고, 계속 malloc함수를 호출하면,

 

언젠가는 힙 영역이 가득 차게 될 것입니다. 그럼 문제가 발생하겠죠.

 

안전을 위해서, 그리고 프로그램의 성능 향상을 위해서라도 free함수를 호출해야 합니다.

 

 

 

 

이제 간단한 예제를 하나 보겠습니다.

 

사용자로부터 문자열을 입력받아서 출력하는 프로그램입니다.

 

다만, 문자열을 입력받기 전에 입력할 문자열의 최대 길이를 먼저 입력받습니다.

 

그리고 그 길이만큼 메모리를 동적 할당해서 문자열을 입력받으면 됩니다.

 

 

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

 

//입력버퍼를 비워주는 함수
void clearBuffer(void){
     //입력버퍼에 문자가 들어있으면,
     //문자를 모두 소모하여 버퍼를 비워버림
     //단순히 사용만하고 저장하지 않으면 비워지게 되는 원리 이용
     while (getchar() != '\n');
}

 

int main(void){
     int size; //문자열의 최대 길이
     char* ptr; //힙 메모리의 할당된 데이터의 주소값을 저장할 변수

 

     fputs("입력할 문자열의 최대 길이를 입력하세요 : ", stdout);
     scanf("%d", &size);

 

     //char형 배열 size개 만큼의 힙 메모리를 할당하여
     //반환형을 char* 로 형변환하여 저장
     ptr = (char*)malloc(sizeof(char)*size);
     //힙 메모리에 공간이 있는지 없는지 확인
     if (ptr == NULL){
          printf("힙 메모리에 공간이 없습니다\n");
          exit(1); //프로그램의 비정상적 종료를 의미
     }

 

     clearBuffer(); //입력버퍼를 비워줌

 

     fputs("문자열 입력 : ", stdout);
     fgets(ptr, size, stdin); //문자열을 입력받아서 힙 메모리에 저장

     printf("저장된 문자열은 %s\n", ptr); //힙 메모리에 저장된 문자열 출력

 

     free(ptr); //할당된 메로리 회수

     return 0;
}

 

 

맨 처음에 clearBuffer 란 함수가 있습니다. 입력버퍼를 비워주는 함수인데요.

 

출력버퍼는 fflush라는 함수에 stdout을 통해 인자를 전달하여 비우게 됩니다.

 

stdin을 인자로 전달하면 입력버퍼도 비울 수 있는거 아니냐? 라고 생각하실 수 있지만,

 

컴파일러마다 되는 경우가 있고, 되지 않는 경우가 있습니다. 그러므로 제가 작성한 함수처럼

 

데이터를 소멸하는 형식으로 입력 버퍼를 비워주셔야 합니다.

 

fflush로 입력버퍼를 비우는 것은 바람직하지 않고, 문제를 발생시킬 수 있으니 주의하세요.

 

 

 

그리고 중간에 malloc함수를 통해 사용자가 입력한 길이만큼의 배열공간을 확보하고 있습니다.

 

바로 밑에는 조건문을 통해 힙 메모리에 정상적으로 적재가 됫는지 검사하고 있습니다.

 

중간에 clearBuffer 함수가 나오네요. 만약 clearBuffer 라는 함수를 주석처리하고 실행해보시면,

 

사용자는 문자열을 입력받을 기회를 놓치게 됩니다.

 

이유는 scanf에서 길이를 입력하고 엔터를 누르게 되는데요,

 

정수는 size에 저장이되고, 엔터를 뜻하는 \n은 입력버퍼에 남아있게 됩니다.

 

이 상황에서 fgets를 호출하면, \n이 전달되므로 사용자가 문자열을 입력할 수 없게 됩니다.

 

 

마지막으로는 free함수에 ptr을 전달함으로써, 힙 영역의 메모리 를 회수하고 있습니다.

 

포인터의 개념이 들어가기 때문에, 조금 어려워 할 수 있으나

 

생각보다 쉬운 개념입니다. 그리고 C언어 뿐만아니라, 각종 분야 및 언어에서도

 

메모리의 구조는 알아두면 유용하기 때문에 꼭 이해하세요.

Comments