Skip to content

31. 포인터와 배열


1. 키워드

  • 가변 길이 배열(VLA: Variable-Length Array)


2. 포인터와 배열 응용하기

  • 이번에는 포인터에 메모리를 할당하여 배열처럼 사용해 보자.
  • 먼저 배열은 소스 코드에서 크기를 지정해서 생성해야 한다.


//         ↓ 배열 크기 지정
int numArr[10];


  • 하지만 다음과 같이 프로그램 실행 중에 원하는 크기만큼 배열을 생성하는 기능(가변 길이 배열)은 GCC에서는 지원하지만, Visual Studio 2015에서는 지원하지 않는다.


#define _CRT_SECURE_NO_WARNING
#include <stdio.h>

int main()
{
    int size;

    scanf("%d", &size); // 배열의 크기를 입력받음

    int numArr[size]; // GCC에서는 사용 가능, Visual Studio 2015에서는 컴파일 에러

    return 0;
}


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


가변 길이 배열

  • C에서는 int numArr[10];처럼 고정된 크기로 배열을 생성하는 것은 가능하지만 int numArr[size];처럼 크기를 동적으로 지정할 수는 없다.
  • 이후 C99 표준이 제정되었고 가변 길이 배열 기능도 추가되었다.
  • GCC, Clang 등의 컴파일러는 가변 길이 배열을 지원하지만 Visual Studio 2015는 지원하지 않는다.


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

  • 포인터를 배열처럼 사용하는 방법은 포인터에 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;
}

// 10
// 20


  • 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); // 동적으로 할당한 메모리 해제


  • 단, 배열 numArr은 한 번 선언하면 끝이지만 포인터 numPtrmalloc 함수로 메모리를 할당했기 때문에 free 함수로 해제해 준다.
  • *numPtr처럼 포인터를 역참조한 것과 numPtr[0] 인덱스 0에 접근한 것은 같은 값을 가져온다.
  • 그리고 numPtr[1]*(numPtr + 1)도 같은 값을 가져오는데, *(numPtr + 1)과 같이 포인터에 값을 더하는 방식을 포인터 연산이라고 한다.


001


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

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


#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;
}

// 5 (입력)
// 0
// 1
// 2
// 3
// 4
// ↑ 5를 입력했으므로 0부터 4까지 값이 들어갔음


  • scanf로 크기를 입력받았다.
  • 그리고 int 타입 크기에 입력받은 크기를 곱하여 메모리를 할당한다.


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 함수로 할당한 메모리를 해제한다.


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


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

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


#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]); // 행 인덱스 0, 열 인덱스 0인 요소에 값 출력
    printf("%d\n", m[2][0]); // 행 인덱스 2, 열 인덱스 0인 요소에 값 출력
    printf("%d\n", m[2][3]); // 행 인덱스 2, 열 인덱스 3인 요소에 값 출력

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

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

    return 0;
}

// 1
// 5
// 2


  • 먼저 다음과 같이 이중 포인터에 2차원 배열의 행 공간에 해당하는 메모리를 할당한다.
  • 이때 행 공간에는 값이 들어가지 않고 열 공간의 메모리 주소가 들어간다.
  • 따라서 sizeof(int)가 아닌 sizeof(int *)처럼 포인터의 크기를 구한 뒤 행 크기 3을 곱해 준다.


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


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


002


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


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


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


003


  • 즉, mpointer to pointer to int이므로 int **m으로 선언하고 m[0], m[1], m[2]point to int이므로 int *가 들어간다.
  • 마지막으로 m[0][0], m[0][1] 등은 int만 들어간다.


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


포인터[행인덱스][열인덱스]
m[0][0] = 1; // 행 인덱스 0, 열 인덱스 0인 요소에 값 할당
m[2][0] = 5; // 행 인덱스 2, 열 인덱스 0인 요소에 값 할당
m[2][3] = 2; // 행 인덱스 2, 열 인덱스 3인 요소에 값 할당

printf("%d\n", m[0][0]); // 행 인덱스 0, 열 인덱스 0인 요소에 값 출력
printf("%d\n", m[2][0]); // 행 인덱스 2, 열 인덱스 0인 요소에 값 출력
printf("%d\n", m[2][3]); // 행 인덱스 2, 열 인덱스 3인 요소에 값 출력


  • 포인터를 다 사용했다면 먼저 열 공간에 해당하는 메모리부터 해제한다.
  • 그러고 나서 행 공간에 해당하는 메모리를 해제한다.
  • free(m);처럼 행 공간에 해당하는 메모리를 먼저 해제해 버리면 열 공간 메모리를 해제할 수 없으므로 주의한다.


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

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


  • 즉, 메모리를 할당할 때 행 → 열 순서로 할당했으므로 해제할 때는 반대로 열 → 행 순서로 해제한다.


6. 입력한 크기만큼 메모리를 할당하여 포인터를 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;
}

// 4 5 (입력)
// 0 1 2 3 4
// 1 2 3 4 5
// 2 3 4 5 6
// 3 4 5 6 7


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


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


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


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


  • 행, 열 크기가 고정되어 있지 않으므로 사용자에게 입력받은 row, col을 활용하여 행과 열을 반복하면서 값을 할당한다.
  • 여기서는 2차원 배열(메모리)의 각 요소에 ij를 더한 값을 할당했다.
  • 값 할당이 끝났으면 다시 행과 열을 반복하면서 2차원 배열의 값을 출력한다.


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차원 배열의 행 공간 메모리 해제

References