profile image

L o a d i n g . . .

Unit 38. 포인터와 배열 응용하기

배열의 크기를 동적으로 지정하려면 어떻게 해야 할까?

크기를 동적으로 지정하려면 포인터를 선언하고 메모리를 할당한 뒤 메모리를 배열처럼 사용해야 한다.

 

38.1 포인터에 할당된 메모리를 배열처럼 사용하기

포인터에 malloc 함수로 메모리를 할당해주면 포인터를 배열처럼 사용할 수 있다

- 자료형 *포인터이름 = malloc(sizeof(자료형) * 크기);

#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

int main()
{
    int *numPtr = malloc(sizeof(int) * 10);    // int 10개 크기만큼 동적 메모리 할당

    numPtr[0] = 10;    // 배열처럼 인덱스로 접근하여 값 할당
    numPtr[9] = 20;    // 배열처럼 인덱스로 접근하여 값 할당

    printf("%d\n", numPtr[0]);    // 배열처럼 인덱스로 접근하여 값 출력
    printf("%d\n", numPtr[9]);    // 배열처럼 인덱스로 접근하여 값 출력

    free(numPtr);    // 동적으로 할당한 메모리 해제

    return 0;
}

int *numPtr = malloc(sizeof(int) * 10);과 같이 int 크기에 10을 곱하여 동적으로 메모리를 할당한다(sizeof(int)를 곱하지 않으면 배열처럼 사용할 수 없음).

 

그리고 배열처럼 [ ] 안에 인덱스를 지정하여 값을 할당하거나 가져올 수 있다.

즉, 배열과 메모리가 할당된 포인터는 생성 방법만 다를 뿐 값을 다루는 방법은 같다.

 - 포인터[인덱스]

int numArr[10];                           // int형 요소 10개를 가진 배열 생성
int *numPtr = malloc(sizeof(int) * 10);   // int 10개 크기만큼 메모리 할당

numArr[0] = 10;    // 배열을 인덱스로 접근하여 값 할당
numPtr[0] = 10;    // 포인터를 인덱스로 접근하여 값 할당

free(numPtr);   // 메모리 해제

단, 배열 numArr은 한 번 선언하면 끝이지만 포인터 numPtr malloc 함수로 메모리를 할당했기 때문에 free함수로 해제해줘야한다.

+ *numPtr처럼 포인터를 역참조한 것과 numPtr[0] 인덱스 0에 접근한 것은 같은 값을 가져온다.

 

 

38.2 입력한 크기만큼 메모리를 할당하여 배열처럼 사용하기

이번에는 사용자가 입력한 크기만큼 메모리를 할당하여 배열처럼 사용해보겠다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

int main()
{
    int size;

    scanf("%d", &size);

    int *numPtr = malloc(sizeof(int) * size);    // (int 크기 * 입력받은 크기)만큼 동적 메모리 할당

    for (int i = 0; i < size; i++)    // 입력받은 크기만큼 반복
    {
        numPtr[i] = i;                // 인덱스로 접근하여 값 할당
    }

    for (int i = 0; i < size; i++)    // 입력받은 크기만큼 반복
    {
        printf("%d\n", numPtr[i]);    // 인덱스로 접근하여 값 출력
    }

    free(numPtr);    // 동적으로 할당한 메모리 해제

    return 0;
}

우선 scanf로 크기를 입력받은 다음, int 크기에 입력받은 크기를 곱하여 메모리를 할당한다.

 

메모리가 준비되었으면 입력받은 크기만큼 반복하면서 값을 할당하고, 다시 입력받은 크기만큼 반복하면서 값을 출력한다. 이때, 배열과 마찬가지로 값을 할당하거나 출력할 때는 [ ]를 사용하여 인덱스로 접근하면 된다.

 

사용이 끝났으면 반드시 free 함수로 할당한 메모리를 해제한다.

 

 

38.3 포인터에 할당된 메모리를 2차원 배열처럼 사용하기

이번에는 포인터에 메모리를 할당하여 세로 크기 3, 가로 크기 4인 2차원 배열처럼 사용해보겠다.

 

1. 자료형 **포인터이름 = malloc(sizeof(자료형 *) * 세로크기);와 같이 세로 공간 메모리 할당

2. 반복문으로 반복하면서 포인터[i] = malloc(sizeof(자료형) * 가로크기);와 같이 가로 공간 메모리 할당

3. 반복문으로 반복하면서 free(포인터[i]);와 같이 가로 공간 메모리 해제

4. free(포인터);와 같이 세로 공간 메모리 해제

#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

int main()
{
    int **m = malloc(sizeof(int *) * 3);   // 이중 포인터에 (int 포인터 크기 * 세로 크기)만큼
                                           // 동적 메모리 할당. 배열의 세로

    for (int i = 0; i < 3; i++)            // 세로 크기만큼 반복
    {
        m[i] = malloc(sizeof(int) * 4);    // (int 크기 * 가로 크기)만큼 동적 메모리 할당.
                                           // 배열의 가로
    }

    m[0][0] = 1;    // 세로 인덱스 0, 가로 인덱스 0인 요소에 값 할당
    m[2][0] = 5;    // 세로 인덱스 2, 가로 인덱스 0인 요소에 값 할당
    m[2][3] = 2;    // 세로 인덱스 2, 가로 인덱스 3인 요소에 값 할당

    printf("%d\n", m[0][0]);    // 1: 세로 인덱스 0, 가로 인덱스 0인 요소의 값 출력
    printf("%d\n", m[2][0]);    // 5: 세로 인덱스 2, 가로 인덱스 0인 요소의 값 출력
    printf("%d\n", m[2][3]);    // 2: 세로 인덱스 2, 가로 인덱스 3인 요소의 값 출력

    for (int i = 0; i < 3; i++)    // 세로 크기만큼 반복
    {
        free(m[i]);                // 2차원 배열의 가로 공간 메모리 해제
    }

    free(m);    // 2차원 배열의 세로 공간 메모리 해제

    return 0;
}

먼저 다음과 같이 이중 포인터에 2차원 배열의 세로 공간에 해당하는 메모리를 할당한다. 이때 세로 공간에는 값이 들어가지 않고 가로 공간의 메모리 주소가 들어간다.

따라서 sizeof(int)가 아닌 sizeof(int *)처럼 포인터의 크기를 구한 뒤 세로 크기 3을 곱한다.

 

이중 포인터에 2차원 배열의 세로 공간에 해당하는 메모리를 할당하는 모습을 그림으로 표현하면 다음과 같다.

이중 포인터에 배열의 세로 공간 할당

 

이제 세로 크기만큼 반복하면서 2차원 배열의 가로 공간에 해당하는 메모리를 할당한다. 가로 공간에는 int형 숫자가 들어갈 것이므로 sizeof(int)에 가로 크기 4를 곱해준다.

 

이중 포인터에 2차원 배열의 가로 공간에 해당하는 메모리를 할당하는 모습을 그림으로 표현하면 다음과 같다.

이중 포인터에 배열의 가로 공간 할당

즉, m은 pointer to pointer to int이므로 int **m으로 선언하고 m[0], m[1], m[2]는 pointer to int이므로 int *가 들어간다(메모리 할당). 마지막으로 m[0][0], m[0][1] 등은 int만 들어간다.

 

이제 2차원 배열을 사용하듯이 [ ] [ ]에 세로 인덱스, 가로 인덱스를 지정하여 값을 할당하거나 가져올 수 있다.

 - 포인터[세로인덱스][가로인덱스]

 

포인터를 다 사용했다면 먼저 가로 공간에 해당하는 메모리부터 해제한다. 그러고 나서 세로 공간에 해당하는 메모리를 해제한다. 즉, 메모리를 할당할 때 세로 → 가로 순서로 할당했으므로 해제할 때는 반대로 가로 → 세로 순서로 해제한다.

 

 

38.4 입력한 크기만큼 메모리를 할당하여 포인터를 2차원 배열처럼 사용하기

이제 사용자가 입력한 만큼 메모리를 할당하여 포인터를 2차원 배열처럼 사용해보겠다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

int main()
{
    int row, col;

    scanf("%d %d", &row, &col);

    int **m = malloc(sizeof(int *) * row);   // 이중 포인터에 (int 포인터 크기 * row)만큼
                                             // 동적 메모리 할당. 배열의 세로

    for (int i = 0; i < row; i++)            // 세로 크기만큼 반복
    {
        m[i] = malloc(sizeof(int) * col);    // (int의 크기 * col)만큼 동적 메모리 할당. 배열의 가로
    }

    for (int i = 0; i < row; i++)    // 세로 크기만큼 반복
    {
        for (int j = 0; j < col; j++)    // 가로 크기만큼 반복
        {
            m[i][j] = i + j;             // 2차원 배열의 각 요소에 i + j 값을 할당
        }
    }

    for (int i = 0; i < row; i++)    // 세로 크기만큼 반복
    {
        for (int j = 0; j < col; j++)    // 가로 크기만큼 반복
        {
            printf("%d ", m[i][j]);      // 2차원 배열의 인덱스에 반복문의 변수 i, j를 지정
        }
        printf("\n");                // 가로 요소를 출력한 뒤 다음 줄로 넘어감
    }

    for (int i = 0; i < row; i++)    // 세로 크기만큼 반복
    {
        free(m[i]);                  // 2차원 배열의 가로 공간 메모리 해제
    }

    free(m);    // 2차원 배열의 세로 공간 메모리 해제

    return 0;
}

1. 먼저 scanf로 세로 크기와 가로 크기를 입력받는다.

2. 그리고 입력받은 세로 크기 변수 row를 활용하여 이중 포인터 m에 2차원 배열의 세로 공간 메모리를 할당한다.

3. 입력받은 가로 크기 변수 col을 활용하여 2차원 배열의 가로 공간 메모리를 할당한다.

4. 세로, 가로 크기가 고정되어 있지 않으므로 사용자에게 입력받은 row, col을 활용하여 세로와 가로를 반복하면서 값을 할당한다.

5. 값 할당이 끝났으면 다시 세로와 가로를 반복하면서 2차원 배열의 값을 출력한다.

6. 마지막으로 입력받은 세로 크기만큼 반복하면서 가로 공간 메모리를 해제한다.

7 그러고 나서 세로 공간 메모리를 해제한다.

복사했습니다!