◇ 메모리의 동적 할당
함수의 반환형은 void형 포인터고 인자는 정수가 들어간다.
예시로
int main(void)
{
void * ptr1 = malloc(4); // 4바이트가 힙 영역에 할당
void * ptr2 = malloc(12); // 12바이트가 힙 영역에 할당
. . . .
free(ptr1); // ptr1이 가리키는 4바이트 메모리 공간 해제
free(ptr2); // ptr2이 가리키는 4바이트 메모리 공간 해제
. . . .
}
다음과 같은 소스코드에서
malloc함수의 활용을 확인해보자.
malloc(4);
malloc(12);
함수는
힙 영역위의 메모리 공간에 4바이트 만큼의 공간을 확보하고
확보된 메모리 공간의 주소값을 반환한다.
힙 영역위의 메모리 공간에 12바이트 만큼의 공간을 확보하고
확보된 메모리 공간의 주소값을 반환한다.
조금 더 구체적으로 얘기를 하면
힙영역의 메모리공간에 다음 그림과 같은 형태의 메모리를 할당하고,
void형 포인터 변수 ptr1에는 주소값 0x12fd10이 담기게 되고,
void형 포인터 변수 ptr2에는 주소값 0x12fd20이 담기게 되는 것이다.
◇ 메모리의 동적 할당의 활용
동적 할당된 메모리 활용에 앞서서 한가지 유념해야 할 것이 있다.
"힙에 할당된 메모리 공간은 포인터 변수를 이용해서만 접근이 가능하다."
malloc함수의 반환형은 void형 포인터이다.
void형 포인터는 단순히 주소값만을 담는 포인터 변수이다.
다음과 같은 코드를 한번 보자.
void * ptr = malloc(sizeof(int)); // int형 변수 크기의 메모리 공간 할당
*ptr = 20; // ptr이 void형 포인터 변수이기에 컴파일 에러
이는 다시
void * ptr = malloc(4); // int형 변수 크기의 메모리 공간 할당
*ptr = 20; // ptr이 void형 포인터 변수이기에 컴파일 에러
위와 같이 컴파일 에러가 나는 이유는
한가지이다.
malloc함수가 반환하는 것은 단순히 메모리 공간의 주소이기 때문이다.
시작 주소만 주어졌을 때,
시작 주소만으로는
몇 바이트까지가 메모리가 할당되었는지, 정수형 형태로 데이터를 저장할지 실수형 형태로 데이터를 저장할지 확인할 수 있는 방법이 없다.
이해를 돕기 위해
malloc 함수를 의인화하여 말을 한다면
"저는 원하시는 크기만큼 메모리 공간을 할당하고 그 메모리의 주소값을 반환하겠습니다.
그러니 어떻게 사용할지는 개발자 본인이 포인터 형의 변환을 통해서 직접 결정하세요."
로 이해할 수 있다.
따라서 다음과 같이 void형으로 반환되는 주소 값을 적절히 형 변환해서 할당된 메모리 공간에 접근 해야 한다.
int * ptr1 = (int *)malloc(sizeof(int)); //int형 변수 크기의 메모리 공간 할당
double * ptr2 = (double *)malloc(sizeof(double)); //double형 변수 크기의 메모리 공간 할당
int * ptr3 = (int *)malloc(sizeof(int)*7); //길이가 7인 int형 배열로 사용할 공간 마련
double * ptr4 = (double *)malloc(sizeof(double)*9); //길이가 9인 double형 배열로 사용할 공간 마련
지금까지의 내용을 바탕으로 malloc함수를 실제로 활용하여 보겠다.
참고로 malloc함수는 메모리 공간의 할당에 실패할 경우 NULL을 반환한다. 따라서 메모리 할당의 성공여부를 확인하는 예외 처리를 하려면
int * ptr = (int *)malloc(sizeof(int));
if(ptr==NULL)
{
// 메모리 할당 실패에 따른 오류의 처리
}
다음과 같이하면 된다.
◇ 동적 할당된 메모리의 해제
만약 malloc함수를 이용한 동적 할당한 메모리 공간을 해제하여 주지않는다고 해보자.
"프로그램 실행 시 할당된 모든 메모리 공간은 프로그램이 종료되면 운영체제에 의해서 전부 해제된다."
위와 같은 이유로
앞서 보인 예제와 같은 경우는
단순하게 할당된 메모리를 한번 사용하고 말기에
큰 문제가 없을 것으로 보인다.
허나 다음과 같은 경우를 생각해보자.
만약 스레드를 통해 백그라운드에서 돌아가는 서비스가
지속적으로 메모리의 해제없이 malloc함수를 통해 메모리를 동적 할당하게 된다면
지속적으로 힙(Heap)영역을 할당 받다가
물리적인 힙영역의 메모리 공간을 넘어서 스택(Stack)영역을
침범하게 될 수 있다.
이와 같은 경우는 단순히 스택영역에 선언된 지역변수의 변경뿐만 아니라
함수의 리턴어드레스(RET)까지 침범하여
프로그램의 흐름의 큰 문제를 보일 수 있다.
또한 이를 통한 힙버퍼오버플로우(Heap Buffer Overflow)라는 기법을 사용하여
보안까지 위협하는 상황에 놓일 수 있다.
그러므로
항상 동적으로 할당된 메모리는
free( )함수를 통해 반드시 메모리의 해제를 해주어야 한다.
'IT > C' 카테고리의 다른 글
함수 포인터 (0) | 2017.10.21 |
---|---|
두 가지 형태의 문자열 표현 (0) | 2017.10.21 |