프로그램이 메모리에 올라가게 되면 Data Segment 와 Code Segment 로 나뉘게 된다.

Heap 과 Stack 은 Data Segment 를 이용하게 된다.


•힙(Heap)은 런 타임시에 크기가 결정되는 요소들이 저장되는 공간이다.

C의 malloc() 함수나 C++의 new 연산자로 메모리 할당이 될 때에는 이 Heap 공간에 메모리가 잡히게 된다.

 
•스택(Stack)은 컴파일시에 크기가 결정되어있는 요소들이 저장되는 공간이다.

함수가 받는 매개 변수나 함수내에서 사용되는 지역변수가 이 Stack 영역에 저장이 된다.

메모리를 가상의 그림으로 나타내면 아래와 같이 나타낼 수 있다.

 ※데이터 영역

전역 변수와 static 변수 저장

※힙 영역

동적 할당되는 데이터 저장
(데이터가 위부터 순차적으로 저장)

  ※스택 영역

지역 변수와 매개변수가 저장
(데이터가 아래부터 순차적으로 저장)

그림 1. 메모리 영역

보통 전역 변수나 static 변수는 heap 의 윗 부분에 위치하고, Stack 은 Data Segment 의 처음부터 할당이 되고,
Heap 은 끝부분 부터 할당이 된다.

예제 코드를 작성하여 돌려보았다.

#include <stdio.h>
int A, B;

void 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);
}

d



Visual Studio 2003에서 작성하였고, 알아보기 쉽게 10진수로 출력을 하였다.
메모리 할당이 된 간격은 틀리지만,
전역 변수와 동적 할당된 포인터의 메모리 할당 순서가 지역 변수의 메모리 할당 순서와 다른것을 확인할 수 있다.

Stack 영역에 올라가는 데이터는 프로그램이 실행되자마자 마로 메모리에 할당이 되고, 함수가 종료되거나,
프로그램이 종료될 때 자동으로 메모리 공간이 해제된다.

그에 반해, Heap 영역에 올라가는 데이터는 프로그램이 실행되는 중간에 메모리에 할당되고, 프로그램이 종료될 때
자동으로 메모리 공간이 해제되지 않는다. 메모리 공간을 해제하면 다시 그 영역을 사용할 수 있다.

따라서 동적으로 할당된 메모리 공간은 C의 free() 함수나 C++의 delete 연산자를 사용하여 메모리 공간을 다시 해제하여 주어야 한다.
그렇지 않을 경우 메모리 누수(leak) 가 발생하게 된다.

그렇다면 메모리 동적할당이 왜 필요한 것인가?
예를들어 다음과 같이 5개의 정수형 데이터를 입력하는 프로그램이 있다고 하자.

#include <stdio.h>

void 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 연산자 사용시에는 데이터 타입을 알 수 있다.)


출처 : 네이버 블로그 : 우기우기(ragcarib) 님, 무단복제입니다. 본자료의 원 작성자분이 요청하시면 삭제하도록 하겠습니다.

'프로그래밍 > 01윈도우즈' 카테고리의 다른 글

Serial 관련 프로그램 참고자료  (0) 2017.09.21
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기