함수 내에서 동적할당 - hamsu naeeseo dongjeoghaldang

JIE0025

Language/C++

[ C++ ] 동적 할당 깊이 파기 (구조 이해)

Kangjieun11 2022. 5. 18. 00:23

동적할당을 알기 전에 먼저 프로그램이 실행되고 동작하는 원리를 알아보자.

CPU, RAM(주기억장치), HDD(하드디스크/보조기억장치), OS(운영체제) 가 있다. 

1) 사용자는 OS(운영체제)를 통해 프로그램의 실행을 요청한다.

2) 하드디스크에 저장되어 있던 program의 정보를 읽어 RAM, 메모리의 프로그램 코드 영역에 올려둔다. 

3) CPU는 program code를 읽어서 메모리를 관리, 명령문을 실행한다. 

4) 프로그램 실행을 위한 동적메모리 할당시 Free Store 영역을 사용 (아래로 확장)

5) system 작동을 위해 CPU가 임시 정보를 스택에 저장시 Free Store 영역을 사용 (위로 확장)

- 만약 Heap과 Stack메모리를 많이 사용해 Free Store영역이 없어지면 메모리 부족 상태가 됨

함수 내에서 동적할당 - hamsu naeeseo dongjeoghaldang

RAM 주기억장치 영역별 데이터에 관한 내용은 과거에도 쓴적이 있다. 

https://jie0025.tistory.com/176?category=1023432 

[C++] C언어 기본 복습 (const , 메모리공간, malloc, free, call-by-value, call-by-reference )

const : 상수화 상수 변수는 항상 선언과 동시에 초기화를 해야 한다. 이후에 값 변경 X const를 통한 포인트 변수 선언 >> 포인터변수가 가리키는 대상이 변할 수 없다. (( )) 괄호를 통해 좀더 쉽게

jie0025.tistory.com

함수 내에서 동적할당 - hamsu naeeseo dongjeoghaldang

그래도 다시 언급하자면,

메모리 영역별 특징

Program code  실행한 프로그램의 코드가 저장
Data 전역변수,  static 변수 >>>> program 종료시까지 남아있음
Heap 동적 할당된 메모리 영역
c++ new 와 c언어 malloc ,
c++ delete와 c언어 free 를 사용해
프로그래머가 할당, 해제 한다. 
Stack 지역변수와 매개변수가 할당, 함수 종료시 자동 소멸

static 메모리 : 데이터영역에 저장, 지속적

heap 메모리 : heap영역에 저장, 지속적, 프로그래머의 관리 필요(해제)

stack 메모리 : stack영역에 저장, 함수 빠져나갈경우 자동 소멸

정적 메모리 : 전역변수, static변수, 지역변수, 매개변수의 저장

동적할당을 하는 이유

1) 일시적으로 많은 메모리를 잡아야 할때 사용한다.

프로그램을 실행하면 RAM 에 올라가서 돌게 되는데 RAM의 메모리 자원은 한정적이다. 

프로세스가 많은 메모리가 필요한 상황에서 처음부터 끝까지 계속 공간을 차지하면 메모리 낭비가 일어난다.

따라서 메모리를 사용할때만 잡고 다시 해지시킴으로 메모리 할당 함으로써 메모리공간을 효율적으로 사용하게 된다. 

2) 함수 리턴 이후에도 메모리 할당이 살아있게 하고 싶은 경우에 사용

(지역변수 사용시 함수 종료후 사라지기 때문에)

3) 그때그때 메모리를 요청하기 위해 : vector 와 같은 것들..

동적할당 특징

1) 꼭 쌍을 맞춰서 사용해야한다. (일부만 해제되는 문제발생 가능)

- malloc 사용시 free

- new사용시 delete

- new[] 사용시 delete[] 

2) 메모리 해제후 포인터의 경우 NULL로 초기화해 에러 방지.

int *data = new int;
int *arr = new int[10];

delete data;
delete[] arr;

2차원 배열의 동적할당

함수 내에서 동적할당 - hamsu naeeseo dongjeoghaldang

mat 는 int**

mat[i] 는 int *

mat[i][k] 는 int형

2차원 동적 배열을 해줄 경우 loop를 돌면서 일일이 할당과 해제를 해야한다. 

근데 그냥 vector를 써버리는게 제일 편함!

메모리의 동적 할당


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

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

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

이렇게 런 타임에 메모리를 할당받는 것을 메모리의 동적 할당(dynamic allocation)이라고 합니다.


malloc() 함수

malloc() 함수는 프로그램이 실행 중일 때 사용자가 직접 힙 영역에 메모리를 할당할 수 있게 해줍니다.

malloc() 함수의 원형은 다음과 같습니다.

원형

#include <stdlib.h>

void *malloc(size_t size);  

malloc() 함수는 인수로 할당받고자 하는 메모리의 크기를 바이트 단위로 전달받습니다.

이 함수는 전달받은 메모리 크기에 맞고, 아직 할당되지 않은 적당한 블록을 찾습니다.

이렇게 찾은 블록의 첫 번째 바이트를 가리키는 주소값을 반환합니다.

힙 영역에 할당할 수 있는 적당한 블록이 없을 때에는 널 포인터를 반환합니다.

주소값을 반환받기 때문에 힙 영역에 할당된 메모리 공간으로 접근하려면 포인터를 사용해야 합니다.

malloc() 함수의 원형에서 볼 수 있는 size_t 타입은 부호없는 정수라고 이해하면 됩니다.


free() 함수

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

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

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

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

이처럼 사용이 끝난 메모리를 해제하지 않아서 메모리가 부족해지는 현상을 메모리 누수(memory leak)라고 합니다.

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 


calloc() 함수

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

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

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

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

calloc() 함수의 원형은 다음과 같습니다.

원형

#include <stdlib.h>

void *calloc(size_t nmemb, size_t size);

calloc() 함수의 첫 번째 인수는 메모리 블록의 개수를 나타내며, 두 번째 인수는 각 블록의 바이트 수를 나타냅니다.

따라서 calloc() 함수는 힙 영역에 size 크기의 메모리 블록을 nmemb개 할당받을 수 있도록 요청합니다.

앞선 예제에서 사용한 malloc() 함수와 다음 예제의 calloc() 함수는 똑같은 동작을 수행합니다.

예제

1. ptr_arr = (int*) malloc(arr_len * sizeof(int));

2. ptr_arr = (int*) calloc(arr_len, sizeof(int));


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() 함수는 만약 기존의 메모리 위치에 충분한 공간이 있다면 바로 이어서 추가 메모리 공간을 할당해 줍니다.

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


연습문제

  • 연습문제1
  • 연습문제2
  • 연습문제3
  • 연습문제4
  • 연습문제5