Study/C언어

처음하시는 분들을 위한 C언어 기초강의 시즌2 - 23 [메모리 구조와 메모리 할당, 배열 동적할당(malloc, realloc, calloc, free)]

2018. 10. 28. 17:00

 제 23강) 메모리 구조와 메모리 할당

오늘은 메모리 구조와 메모리 할당에 대해서 알아보는 시간입니다.

 

 1) 메모리 구조

우리는 지금까지 변수를 써오면서 "메모리 공간에 할당"된다고 배웠습니다.

그럼 이 메모리는 어떠한 구조로 이루어져 있는가에 대해 고찰해봅시다.

메모리의 구조는 위의 그림과 같은 4가지 영역으로 나뉘어있습니다.

 

 코드 영역(Code Area)
코드가 있는 영역으로 실행하는 프로그램의 코드에 관련된 명령들이 있는 영역입니다.

 

 데이터 영역(Data Area)
프로그램이 생성되었을 때 생성되고, 종료되었을 때 사라지는 데이터가 저장됩니다.
(전역 변수, 혹은 정적 변수, 배열, 구조체 등이 저장됩니다.)

 

 힙 영역(Heap Area)
사용자에게 할당되는 영역으로 직접 관리하는 영역입니다.
동적으로 메모리를 할당하거나 참조 변수가 이 영역에 있습니다.

 

 스택 영역(Stack Area)
데이터가 가장 나중에 들어온 순서부터 맨위에 있는 영역으로 지역변수나 매개변수, 반환 값들이 저장됩니다.
(Last In, First Out, 나중에 들어온 데이터가 먼저 나갑니다.)

 

 

 2) 메모리 동적 할당

메모리 동적 할당이라 함은 메모리를 Dynamic 하게 할당함을 뜻합니다.

간단히 말하자면 배열을 원하는 만큼 할당하는 것이지요.

즉, 다음과 같은 프로그램이 가능하다는 것입니다.

수를 입력 받아서 그 만큼 메모리를 할당 할 수 있다는 것이지요.

 

그래서 배열 동적할당이라고도 합니다.

 

 2 - 1) malloc

메모리를 동적으로 할당하는데 쓰이는 함수인 malloc 입니다.

#include <stdlib.h>        // malloc을 사용하기 위함
 
// 기본 형태
void* malloc(size_t size);    // size 크기 만큼 메모리를 할당함
 
// 예)
int* arr = (int*)malloc(sizeof(int) * 6);    // 길이가 6인(int형) 동적 메모리공간을 할당함

기본 형태는 "void*"형 입니다만 malloc앞에 캐스팅연산을 하여 사용하면 됩니다.

(이때 반환되는 값은 성공적으로 배열을 만들었으면 해당 배열의 주소값이, 실패했다면 NULL값이 반환됩니다.)

할당할 때 크기는 할당하는 배열의 형에 맞게 sizeof를 한 다음에 원하는 길이만큼 곱해주면 됩니다.

 

#include <stdlib.h>        // malloc을 사용하기 위함
 
// 예)
int* arr = (int*)malloc(sizeof(int) * 6);          // 길이가 6인(int형) 동적 메모리공간을 할당함
 
char* arr = (char*)malloc(sizeof(char) * 12);      // 길이가 12인(char형) 동적 메모리공간을 할당함
 
double* arr = (double*)malloc(sizeof(double) * 4); // 길이가 4인(double형) 동적 메모리공간을 할당함

이렇게 사용하면 됩니다.

 

그리고 malloc으로 할당한 배열은 free 함수를 이용해서 해제를 해야합니다.

 

 2 - 2) free

위에서 동적으로 할당한 배열을 해제(제거)하여봅시다.

#include <stdlib.h>        // malloc을 사용하기 위함
 
// 기본 형태
void free(void* ptr);
 
// 예)
int* arr = (int*)malloc(sizeof(int) * 6);        // 길이가 6인(int형) 동적 메모리공간을 할당함
free(arr);                                        // 메모리 공간 해제

위와 같이 free를 이용하여 해제하면 됩니다.

 

이 해제 과정은 선택이 아닌 필수입니다.

그렇지 않으면 동적으로 할당한 메모리 공간이 해제되지 않아 메모리 누수가 발생합니다.

(즉, 자동으로 메모리가 비워지지 않습니다.)

 

이제 자신이 입력한 수 만큼 배열을 생성하는 예제를 만들어봅시다.

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int* arr;
    int size;
    
    printf("수를 입력하세요: ");
    scanf("%d", &size);
    
    arr = (int*)malloc(sizeof(int) * size);
    
    if (arr == NULL)
        printf("배열이 생성되지 않음\n");
    else
    {
        for (int i = 0; i < size; i++)
            arr[i] = i;
        for (int i = 0; i < size; i++)
            printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    free(arr);
    printf("메모리 해제 완료\n");
    return 0;
}

항상 사용한 다음에 꼭 메모리를 해제해야합니다.(24번줄)

 

 

 2 - 3) malloc(2차원배열)

malloc을 이용하여 2차원배열도 동적으로 만들 수 있습니다.

하지만 1차원배열과는 달리 복잡합니다.

#include <stdlib.h>        // malloc을 사용하기 위함
 
// 5 * 3 배열을 동적으로 할당하기
 
int **arr;                 // 생성할 2차원배열
arr = (int**)malloc(sizeof(int*) * 5);             // 5행을 생성한 뒤
for (int i = 0; i < 5; i++)
{
    arr[i] = (int*)malloc(sizeof(int) * 3);        // 각각의 행에 3열씩 추가합니다.
}
 
// 생성한 메모리 해제
for (int i = 0; i < 5; i++)
{
    free(arr[i]);     // 각 행별로 선언된 열을 해제
}
free(arr);            // 행을 

이중포인터로 먼저 이차원배열을 생성할 것을 마련하고(5번줄)

행을 먼저 생성합니다.(6번줄)

그리고 각 행에 각각 열을 생성해줍니다.(7~10번줄)

사용한 후에 메모리를 해제해줍니다.(13~17번줄)

 

 2 - 4) realloc

malloc을 이용하여 만든 배열을 크기를 수정하여 다시 할당해주는 함수입니다.

#include <stdlib.h>        // realloc을 사용하기 위함
 
// 기본 형태
void* realloc(void* ptr, size_t size);    // ptr을 size크기로 재할당 함
 
// 예)
int* arr = (int*)malloc(sizeof(int) * 6);    // 길이가 6인(int형) 동적 메모리공간을 할당함
arr = (int*)realloc(arr, sizeof(int) * 8);   // arr을 길이가 8인(int형) 배열로 다시 재할당함

이때 반환값은 malloc과 같지만 한 가지 유의사항이 있습니다.

 

7번줄에서 8번줄로 넘어갈때 realloc이 실패할 경우에는 기존의 주소값을 잃어버리게 됩니다.

그래서 기존에 할당된 메모리를 free할 수 없어서 메모리 누수가 발생하게 됩니다.

 

그래서 realloc을 쓸때는 다음의 방법을 고려해야합니다.

// 기존의 배열을 남긴다.
 
#include <stdlib.h>        // malloc을 사용하기 위함
 
// 예)
int* arr = (int*)malloc(sizeof(int) * 6);    
int* arr2 = (int*)realloc(arr, sizeof(int) * 8);   // 기존의 arr을 놔두고 새롭게 arr2를 만든다.
if (arr2 != NULL)
{
    // 배열이 성공적으로 재할당되었다면 arr을 새로 재할당된 arr2를 할당한다.
    arr = arr2;
}

배열을 재할당받아야 한다면 새로 배열을 만들고 그 후에 성공했을때 예전배열과 바꿔치기를 하는 것이지요.

(포인터이기에 가능한 연산입니다.)

만약에 실패했다면 기존의 배열을 그대로 가지고 있는 것이 됩니다.

 

이제 재할당 예제를 봅시다.

#include <stdio.h>
#include <stdlib.h>
#define BASIC_SIZE 5
 
void printArray(int _arr[], int _size)
{
    for (int i = 0; i < _size; i++)
    {
        printf("%d", _arr[i]);
        if (i != _size - 1)
            printf(", ");
        else
            printf("\n");
    }
}
 
int main()
{
    int* arr = (int*)malloc(sizeof(int) * BASIC_SIZE);
    int adder = 1;
    int counter = 0;
    int input = 0;
 
    printf("(-1 선택시 배열 출력)\n");
    do
    {
        printf("수를 입력하세요 : ");
        scanf("%d", &input);
 
        if (input == -1)
            printArray(arr, counter);
        else if (counter >= BASIC_SIZE * adder - 1)
        {
            printf("배열 재할당\n");
            adder++;
            int* arr2 = (int*)realloc(arr, sizeof(int) * BASIC_SIZE * adder);
            if (arr2 != NULL)
            {
                arr = arr2;
                arr[counter++] = input;
            }
            else
            {
                printf("배열 재할당 실패\n");
                adder--;
                break;
            }
        }
        else
            arr[counter++] = input;
    } while (input != 0);
 
    free(arr);
    printf("메모리 해제 완료\n");
    return 0;
}

정말 간단한 예제입니다.

처음에 배열은 BASIC_SIZE 크기인 5만큼 할당되어있습니다.

여기에 계속 수를 넣게 되면 counter가 증가하게 됩니다.

이 counter가 BASIC_SIZE * adder - 1보다 크거나 같게 된다면 즉, 배열 크기의 끝에 도달하게 되면 adder를 1증가 시킨 후에 배열을 BAIC_SIZE * adder의 크기로 재할당하게 됩니다.

이전의 배열이 5 * 1이었다면 이후의 배열의 크기는 adder가 1증가하기 때문에 5 * 2인 10의 크기를 가진 배열로 재탄생하게 됩니다.

 

 

 2 - 4) calloc

malloc과 정말 비슷하지만 아주 살짝 다릅니다.

#include <stdlib.h>        // calloc을 사용하기 위함
 
// 기본 형태
void* calloc(size_t count, size_t type);    // type형의 크기로 count 길이만큼 할당
 
// 예)
int* arr = (int*)calloc(6, sizeof(int));    // 길이가 6인(int형) 동적 메모리공간을 할당함
위의 선언은 int* arr = (int*)malloc(sizeof(int) * 6); 과 같습니다.
 
char* arr = (char*)calloc(6, sizeof(char));
위의 선언은 char* arr = (char*)malloc(sizeof(char) * 6); 과 같습니다.

malloc과 같지만 malloc은 길이를 곱해주는 방식으로 정했었다면, calloc은 인수로 직접 받게됩니다.

 

그 이후에 한 가지 더 다른점이 있는데

#include <stdio.h>
#include <stdlib.h>
#define BASIC_SIZE 6
 
void printArray(char str[], int _arr[], int _size)
{
    printf("%s: ", str);
    for (int i = 0; i < _size; i++)
    {
        printf("%d", _arr[i]);
        if (i != _size - 1)
            printf(", ");
        else
            printf("\n");
    }
    printf("\n");
}
 
int main()
{
    // BASIC_SIZE = 6
    int* arr_malloc = (int*)malloc(sizeof(int) * BASIC_SIZE);
    int* arr_calloc = (int*)calloc(BASIC_SIZE, sizeof(int));
 
    printArray("arr_malloc", arr_malloc, BASIC_SIZE);
    printArray("arr_calloc", arr_calloc, BASIC_SIZE);
 
    free(arr_malloc);
    free(arr_calloc);
    return 0;
}

위의 예제에서는 둘다 배열을 생성하고 나서 바로 출력을 하는 예제입니다.

 

이렇게 malloc으로 할당한 배열은 쓰레기 값이 들어가지만

calloc으로 할당한 배열은 0으로 자동 초기화가 됨을 알 수 있습니다.

 

그런데 GCC에서는 둘다 0으로 초기화하여 처리하는 것 같습니다.

 

 

 다음 시간에는

오늘은 메모리 동적 할당에 대해서 알아보았습니다.

다음 시간에는 구조체에 대해서 알아볼것입니다.

 

 

 

저작자표시 비영리 변경금지 (새창열림)

'Study > C언어' 카테고리의 다른 글

처음하시는 분들을 위한 C언어 기초강의 시즌2 - 25 [공용체와 열거형(union, enum)]  (0) 2018.10.28
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 24 [구조체와 형식 정의 지정자(struct, typedef)]  (0) 2018.10.28
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 22 [다차원배열2 - 다차원배열과 포인터, 이중 포인터, 다중 포인터]  (0) 2018.10.17
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 21 [다차원배열1 - 다차원배열의 기본(2차원, 3차원)]  (0) 2018.10.17
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 20 [함수 포인터]  (0) 2018.10.17
'Study/C언어' 카테고리의 다른 글
  • 처음하시는 분들을 위한 C언어 기초강의 시즌2 - 25 [공용체와 열거형(union, enum)]
  • 처음하시는 분들을 위한 C언어 기초강의 시즌2 - 24 [구조체와 형식 정의 지정자(struct, typedef)]
  • 처음하시는 분들을 위한 C언어 기초강의 시즌2 - 22 [다차원배열2 - 다차원배열과 포인터, 이중 포인터, 다중 포인터]
  • 처음하시는 분들을 위한 C언어 기초강의 시즌2 - 21 [다차원배열1 - 다차원배열의 기본(2차원, 3차원)]
Eskeptor
Eskeptor
Hello WorldEskeptor 님의 블로그입니다.
Eskeptor
Hello World
Eskeptor
전체
오늘
어제
  • 분류 전체보기 (138)
    • Computer (5)
      • Linux (1)
      • Hardware (2)
      • Software (0)
      • Tips (1)
      • Website (0)
    • Mobile (1)
      • Application (1)
    • Study (108)
      • Android (9)
      • C언어 (45)
      • C++ (17)
      • Unity 5(유니티5) (11)
      • Qt 프로그래밍 (2)
      • MFC (12)
      • C#, Winform (12)
    • My World (24)
      • OpenPad(Android) (12)
      • 한글 패치 (1)
      • C#으로 만든 귀요미들 (5)
      • MFC로 만든 귀요미들 (6)
    • Life Goes On (0)
      • Hip Hop (0)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

인기 글

태그

  • 테트리스
  • c++11
  • Tetris
  • Unity
  • Java
  • C언어
  • 기초
  • Android
  • 프로그래밍
  • 슈팅게임
  • 강의
  • 배열
  • 왕초보
  • 자바
  • openpad
  • MFC
  • C++
  • 강좌
  • 알고리즘
  • 자료구조
  • 오픈패드
  • 유니티
  • 포인터
  • 초보
  • 기본
  • 비행기
  • 만들기
  • 메모장
  • 안드로이드
  • C#

최근 댓글

최근 글

hELLO · Designed By 정상우.
Eskeptor
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 23 [메모리 구조와 메모리 할당, 배열 동적할당(malloc, realloc, calloc, free)]
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.