2009. 12. 19. 13:14 Programming/자료구조
Heap 과 Stack 영역
[출처]착하게...삽시다! | 우기우기 http://blog.naver.com/ragcarib/12005303968
프로그램이 메모리에 올라가게 되면 Data Segment 와 Code Segment 로 나뉘게 된다.
Heap 과 Stack 은 Data Segment 를 이용하게 된다.
- 힙(Heap)은 런 타임시에 크기가 결정되는 요소들이 저장되는 공간이다.
C의 malloc() 함수나 C++의 new 연산자로 메모리 할당이 될 때에는 이 Heap 공간에 메모리가 잡히게 된다.
- 스택(Stack)은 컴파일시에 크기가 결정되어있는 요소들이 저장되는 공간이다.
함수가 받는 매개 변수나 함수내에서 사용되는 지역변수가 이 Stack 영역에 저장이 된다.
메모리를 가상으로 그림으로 그려보면 다음과 같이 나타낼 수 있다.
※데이터 영역 전역 변수와 static 변수 저장 ---------------------------------- ※힙 영역 동적 할당되는 데이터 저장 (데이터가 위부터 순차적으로 저장)
|
※스택 영역 지역 변수와 매개변수가 저장 (데이터가 아래부터 순차적으로 저장)
|
보통 전역 변수나 static 변수는 heap 의 윗 부분에 위치하고, Stack 은 Data Segment 의 처음부터 할당이 되고,
Heap 은 끝부분 부터 할당이 된다.
예제 코드를 작성하여 돌려보았다.
#include <stdio.h>
int A, B;
main()
{
int a = 0;
int b = 0;
int *p1 = NULL;
int *p2 = NULL;
p1 = (int*)malloc(sizeof(A));
p2 = (int*)malloc(sizeof(A));
printf("전역 변수의 주소값 출력\n");
printf("%d\n", &A);
printf("%d\n", &B);
printf("동적할당된 포인터의 주소값 출력\n");
printf("%d\n", p1);
printf("%d\n", p2);
printf("지역 변수의 주소값 출력\n");
printf("%d\n", &a);
printf("%d\n", &b);
free(p1);
free(p2);
}
Visual Studio 2003에서 작성하였고, 알아보기 쉽게 10진수로 출력을 하였다.
메모리 할당이 된 간격은 틀리지만,
전역 변수와 동적 할당된 포인터의 메모리 할당 순서가 지역 변수의 메모리 할당 순서와 다른것을 확인할 수 있다.
Stack 영역에 올라가는 데이터는 프로그램이 실행되자마자 마로 메모리에 할당이 되고, 함수가 종료되거나, 프로그램이 종료될 때 자동으로 메모리 공간이 해제된다.
그에 반해, Heap 영역에 올라가는 데이터는 프로그램이 실행되는 중간에 메모리에 할당되고, 프로그램이 종료될 때
자동으로 메모리 공간이 해제되지 않는다. 메모리 공간을 해제하면 다시 그 영역을 사용할 수 있다.
따라서 동적으로 할당된 메모리 공간은 C의 free() 함수나 C++의 delete 연산자를 사용하여 메모리 공간을 다시 해제하여 주어야 한다.
그렇지 않을 경우 메모리 누수(leak) 가 발생하게 된다.
그렇다면 메모리 동적할당이 왜 필요한 것인가?
예를들어 다음과 같이 5개의 정수형 데이터를 입력하는 프로그램이 있다고 하자.
#include <stdio.h>
main()
{
int arr[5];
int i = 0;
for(i = 0; i < 5; i++) {
printf("정수 입력 : ");
scanf("%d", &arr[i]);
}
for(i = 0; i < 5; i++) {
printf("%d번째 입력 데이터 : %d\n", i+1, arr[i]);
}
}
이 프로그램은 죽었다 깨어나도 5개의 정수형 데이터 밖에 입력을 하지 못한다. 이제 이 프로그램을 실행 시 마다 입력 되는 데이터의
수가 다르게 작성을 하고 싶어서 다음과 같이 프로그램을 작성하였다.
#include <stdio.h>
void func(int n);
main()
{
int *p = NULL;
int i = 0, n = 0;
printf("입력할 데이터의 수를 입력 : ");
scanf("%d", &n);
func(n);
}
void func(int n)
{
int arr[n]; // Compile Error
int i = 0;
for(i = 0; i < n; i++){
printf("정수 입력 : ");
scanf("%d", &arr[i]);
}
for(i = 0; i < n; i++)
printf("%d번째 입력 데이터 : %d\n", i+1, arr[i]);
}
-------------------------------------------------------------------------------------------------------------------
func() 함수 내의 arr 이라는 배열은 func 함수가 실행된 후 종료될 때 스택 메모리 영역에서 메모리가 해제 되므로
출력까지 func 함수 내에서 처리하였다. 주의깊게 보아야 할 부분은 배열 선언 시 인덱스 값으로 변수가 올 수 없다는 것이다.
(배열 선언시의 인덱스 값에는 상수만이 올 수 있다.)
-------------------------------------------------------------------------------------------------------------------
그럼 다음과 같은 문제를 포인터를 사용한 동적 메모리 할당으로 해결해보면 다음과 같은 방식으로 작성할 수 있다.
포인터와 배열의 차이점과 공통점을 어느정도 알고 있어야 한다.
#include <stdio.h>
#include <stdlib.h>
main()
{
int *p = NULL;
int i = 0, n = 0;
printf("입력할 데이터의 수를 입력 : ");
scanf("%d", &n);
p = (int*)malloc(sizeof(int)*n);
for(i = 0; i < n; i++){
printf("정수 입력 : ");
scanf("%d", (p)+i);
}
for(i = 0; i < n; i++)
printf("%d번 입력 데이터 : %d\n", i+1, *((p)+i));
free(p); // Important!!
}
-------------------------------------------------------------------------------------------------------------------
main() 함수 내에 포인터 변수 p가 선언되어 있고, 이를 입력받은 n의 갯수만큼 int 형으로 동적 메모리 할당을 하고 있다.
이후 포인터 변수 p를 이용하여 데이터를 입력받고 출력 한 후 할당받은 메모리 공간을 해제한다.
-------------------------------------------------------------------------------------------------------------------
동적 메모리 할당을 사용하면 위와 같은 이점을 활용할 수 있게된다.
C++에서의 new 와 delete 연산자를 이용한 메모리 할당/해제 방식은
int *p;
p = new int[데이터갯수];
delete[] p;
와 같은 방법으로 할당과 해제를 한다.
예를들어 '데이터 갯수' 부분에 5를 입력하게 되면
malloc(sizeof(int)*5) 와 같은 크기를 할당하게 된다. (new 연산자 사용시에는 데이터 타입을 알 수 있다.)