본문 바로가기
공부

메모리의 관리(구조/스택프레임/동적할당)

by MY블로그 2022. 10. 26.

메모리의 구조

프로그램이 실행되려면 우선 프로그램이 메모리에 로드(load)되어야 한다.

프로그램에서 사용되는 변수들을 저장할 메모리가 필요하다.

이에따라 컴퓨터의 운영체제는 프로그램의 실행을 위하여 다양한 메모리공간이 존재한다.

프로그램이 운영체제로부터 할당받는 메모리의 영역은 아래와 같다.

출처 : http://tcpschool.com/c/c_memory_structure

1.코드 영역 (CODE)

2.데이터 영역 (DATA)

3.스택 영역 (STACK)

4.힙 영영 (HEAP)

참조 블로그 

https://m.blog.naver.com/rhkdals1206/221519863419

 

C/C++ :: 메모리영역, 상수, 전역/정적 변수

* 프로세스 메모리 영역 - 프로세스 : 현재 실행중인 프로그램 * Kernel 영역 (커널) - 윈도우즈가 사용하...

blog.naver.com

 

코드 영역 (CODE)

메모리의 코드 영역은 실행할 프로그램의 코드가 저장되어있는 영역이다.

프로그램의 코드영역은 텍스트영역이라고도 한다.

CPU는 코드 영역에 저장된 명령어를 하나씩 가져가서 처리한다.

 

데이터 영역 (DATA)

프로그램의 전역 변수와 정적(static=고정된)변수가 저장되는 공간이다.

데이터영역은 프로그램이 시작될때 생기며 프로그램종료시 소멸된다.

 

스택 영역 (STACK)

메모리의 스택영역은 함수의 호출과 관계되는 지역변수 & 매개변수 의 저장공간

함수의 호출과 함께 할당되며 함수호출이 완료되면 소멸한다.

이영역에 저장되는 함수의 호출 정보를 *스택프레임(stack frame)이라한다.

메모리의 높은 주소에서 낮은 주소의 방향으로 할당이 된다.

스택 영역은 푸시(push)동작으로 데이터를 저장하고, 팝(pop) 동작으로 꺼낸다.

스택은 **후입선출(Last-In First-Out/줄여서 LIFO) 방식이다.

**후입선출이란 -> 저장순서중 가장나중에 저장된 데이터가 가장 먼저 나가는것.

*스택프레임(stack frame) 의 동작 방식

*스택프레임(stack frame) 의 동작 방식 예시

int main(void)
{
    func1();  // func1() 호출
    return 0;
}

void func1()
{
    func2();  // func2() 호출
}

void func2()
{
}

스텍 프레임의 변화 (출처 : http://tcpschool.com/c/c_memory_stackframe)

 

스텍 프레임의 변화 (출처 : http://tcpschool.com/c/c_memory_stackframe)

 

Step 1. 프로그램실행 > 먼저 main()함수가 호출되어 main() 함수의 스택프레임 저장

Step 2. func1() 함수를 호출하면 해당 함수의 매개변수, 반환 주소값, 지역 변수 등의 스택 프레임이 스택에 저장

Step 3. func2() 함수를 호출하면 해당 함수의 스택 프레임이 추가로 스택에 저장

Step 4. func2() 함수의 모든 작업이 완료되어 반환되면, func2()의 스택프레임만 스택에서 제거

Step 5. func1() 함수의 호출이 종료되면,func1()의 스택프레임이 스택에서 제거

Step 5. main() 함수의 모든 작업이 완료되면,main()의 스택프레임이 스택에서 제거되며 프로그램이 종료

위처럼 스택은 후입선출 방식으로 동작된다.

 

스택 오버플로우(stack overflow)

위처럼 함수의 재귀 호출이 무한히 반복되면, 해당 프로그램은 스택오버플로우에 의해 종료된다.

해당 스택 영역을 넘어가도 데이터가 저장될경우 프로그램은 오동작이되거나 보안상 취약점을 가질수있다.

C언어에서는 실행 중인 프로그램에서 스택 오버플로우가 발생하면, 에러가 발생하고 곧바로 강제 종료시킨다.

스텍 프레임의 변화 (출처 : http://tcpschool.com/c/c_memory_stackframe)

 

 

힙 영역 (HEAP)

메모리의 힙 영역은 사용자가 직접 관리할 수 있는(해야만하는)영역이다.

사용자에 의하여 메모리 공간이 *동적(움직일수있는)으로 할당&해제된다.

메모리의 낮은 주소에서 높은 주소의 방향으로 할당 된다.

 

*메모리의 동적 할당(dynamic allocation)

데이터 영역과 스택영역에 할당되는 메모리의 크기는 컴파일 타임(compile time)에 미리 결정된다.

하지만 힙(heap)영역의 크기는 프로그램이 실행되는 도중인(run time)사용자가 직접 결정하게된다.

런 타임에 메모리를 할당받는 것을 메모리의 동적할당 이라고 한다.

 

사용자가 직접 힙 영역에 메모리를 할당할수있는 함수 = malloc() 함수 이다.

malloc() 함수의 원형
#include <stdlib.h>
void *malloc(size_t size); ->size_t 타입은 부호가 없는 정수이다.

malloc() 함수는 인수로 할당받고자 하는 메모리의 크기를 바이트(byte) 단위로 전달받는다.
전달받은 메모리의 크기에 맞고, 아직 할당되지 않은 적당한 블록을 찾는다.
이렇게 찾은 블록의 첫 번째 바이트를 가리키는 주소값을 반환 한다.

힙 영역에 할당할 수 있는 적당한 블록이 없을 경우는 널(NULL) 포인터를 반환한다.
주소값을 반환받기 때문에 힙 영역에 할당된 메모리 공간으로 접근하려면 포인터를 사용한다.

 

[1] free() 함수

free() 함수는 힙 영역에 할당받은 메모리 공간을 다시 운영체제로 반환해 주는 함수이다.

데이터 영역이나 스택 영역에 할당되는 메모리의 크기는 컴파일 타임에 결정되어, 프로그램이 실행되는 내내 고정이 된다.

하지만 메모리의 동적 할당으로 힙 영역에 생성되는 메모리의 크기는 런 타임(run time) 내내 변화 된다.

따라서 free() 함수를 사용하여 다 사용한 메모리를 해제해 주지 않으면, 메모리가 부족해지는 현상이 발생한다.

사용이 끝난 메모리를 해제하지 않아서 메모리가 부족해지는 현상을 메모리누수(memoryleak)이라고 한다.

free()함수의 원형

#include <stdlib.h>
void free(void *ptr);

free() 함수는 인수로 해제하고자 하는 메모리 공간을 가리키는 포인터를 전달 받는다.
인수의 타입이 void형 포인터로 선언되어 있으므로, 어떠한 타입의 포인터라도 인수로 전달될 수 있다.
[크기가 고정된 배열이 아닌 런 타임에 크기가 결정되는 배열을 생성하는 예제]

ptr_arr = (int*) malloc(arr_len * sizeof(int)); // 메모리의 동적 할당  

if (ptr_arr == NULL) // 메모리의 동적 할당이 실패할 경우
{
    printf("메모리의 동적 할당에 실패했습니다.\n");
    exit(1);
}  

printf("동적으로 할당받은 메모리의 초깃값은 다음과 같습니다.\n");

for (i = 0; i < arr_len; i++)
{
    printf("%d ", ptr_arr[i]);
}
free(ptr_arr);       // 동적으로 할당된 메모리의 반환

[실행결과]
동적으로 할당받은 메모리의 초기값은 다음과 같다.
0 0 0

[2] calloc() 함수

calloc() 함수는 malloc()함수와 마찬가지로 힙 영역에 메모리를 동적할당 해주는 함수이다.

malloc() 함수와 다른 점은 할당하고자 하는 메모리의 크기를 두 개의 인수로 나누어 전달받는 점이다.

또한, calloc() 함수는 메모리를 할당받은 후에 메모리의 모든 비트값을 전부 0으로 초기화 해준다.

calloc() 함수도 malloc() 함수와 마찬가지로 free() 함수를 통해 할당받은 메모리를 해제해 주어야 한다.

[colloc() 함수의 원형]
#include <stdlib.h>
void *calloc(size_t nmemb, size_t size);

colloc() 함수의 첫 번째 인수는 메모리 블록의 개수를 나타내며 두번째 인수는 각 블록의 바이트 수 이다.
따라서 colloc() 함수는 힙 영역에 size 크기의 메모리 블록을 nmemb개 할당 받도록 요청한다.

[예제]
1. ptr_arr = (int*) malloc(arr_len * sizeof(int));
2. ptr_arr = (int*) calloc(arr_len, sizeof(int));

 

[3] realloc() 함수

realloc() 함수는 이미 할당된 메모리의 크기를 바꾸어 재할당 시에 사용하는 함수이다.

[realloc() 함수의 원형]
#include <stdlib.h>
void *realloc(void *ptr, size_t size);

realloc() 함수의 첫 번째 인수는 크기를 바꾸고자 하는 메모리 공간을 가리키는 포인터를 전달받는다.
두 번째 인수로는 해당 메모리 공간에 재할당할 크기를 전달 한다.
따라서 첫 번째 인수로 NULL이 전달되면, malloc()함수와 정확히 같은 동작을 하게 된다.

[예제] - 런타임중 크기가 결정된 배열의 크기를 realloc() 함수를 사용해 다시한번 늘려주는 예제

ptr_arr = (int*) malloc(arr_len * sizeof(int)); // 메모리의 동적 할당  
if (ptr_arr == NULL) // 메모리의 동적 할당이 실패할 경우
{
    printf("메모리의 동적 할당에 실패했습니다.\n");
    exit(1);
}  

printf("동적으로 할당받은 메모리의 초깃값은 다음과 같습니다.\n");

for (i = 0; i < arr_len; i++)
{
    printf("%d ", ptr_arr[i]);
}

total_len = arr_len + add_len;

ptr_arr = (int*) realloc(ptr_arr, (total_len * sizeof(int))); // 메모리의 추가 할당

if (ptr_arr == NULL) // 메모리의 추가 할당에 실패할 경우

{
    printf("메모리의 추가 할당에 실패했습니다.\n");
    exit(1);
}  

printf("\n추가로 할당받은 메모리의 초깃값은 다음과 같습니다.\n");

for (i = 0; i < total_len; i++)
{
    printf("%d ", ptr_arr[i]);
}
free(ptr_arr); // 동적으로 할당된 메모리의 반환

[예제의 실행 결과]
동적으로 할당받은 메모리의 초기값은 다음과 같습니다.
0 0 0 
추가로 할당받은 메모리의 초기값은 다음과 같습니다.
0 0 0 0 0

realloc() 함수는 만약 기존의 메모리 위치에 충분한 공간이 있다면 바로 이어서 추가 메모리 공간을 할당해 준다.

하지만 기존의 메모리 위치에 충분한 공간이 없으면 메모리의 다른 공간에 기존의 데이터를 복사한 후, 이어서 추가 메모리 공간을 할당하게 된다.

'공부' 카테고리의 다른 글

상수와 포문을 이용한 별찍기 과제  (0) 2022.10.27
달팽이 배열 알고리즘  (0) 2022.10.26
스트링 활용 빙교 풀이  (0) 2022.10.26
스트링 활용 빙고  (0) 2022.10.26
숫자 빙고 게임  (0) 2022.10.24

댓글