본문 바로가기
c언어

c언어 다차원 배열과 포인터에 대하여 알아보기

by 개발자 L 2023. 2. 8.
반응형

c언어 다차원 배열과 포인터에 대하여 알아보기

네 안녕하세요, 이번 포스팅에서는 다차원 배열 포인터에 대하여 알아보도록 하겠습니다.

우리가 배열을 쓰게 되면 1차원 배열만 쓰는 것이 아니라,

2차원 배열, 그리고 공학 계열의 알고리즘을 이용하여 인공지능을 학습을 시킬 때는

그 이상의 배열을 쓰기도 합니다.

하지만 데이터 사이언티스트나 고도의 딥러닝 등을 하지 않는 이상은 3차원 그 이상의 배열을 쓸 일은 거의 없으니

2차원 배열을 위주로 다룰 생각입니다.

그래서 2차원 이상의 배열에서 원소를 간접 참조를 할 때,

특히 2차원 배열에서 포인터를 어떻게 사용을 하는지 알아보도록 하겠습니다.

 

1. 2차원 배열과 포인터

우선 2차원 배열이 하나 있다고 가정을 해봅시다.

우리가 2차원 배열을 선언할 때는 이런 식으로 선언을 합니다.

int a[3][3];

이렇게 선언을 하면 3행 3열의 2차원 배열이 하나 생성이 됩니다.

그리고 이러한 배열은 시작 주소를 가지고 있습니다.

이 시작 주소는 프로그래밍 언어가 채택한 메모리 저장 방법에 따라서 갈립니다.

메모리 저장 방법은 크게 2가지가 있습니다.

  • 행우선 방법
  • 열우선 방법

이 중에서 c언어는 전자의 방법을 채택하고 있으며,

제일 첫 행에 있는 가장 첫 번째 인덱스 주소인 '&[0][0]'이 2차원 배열의 이름이 되고,

1차원 배열에서 제일 첫 번째에 있는 원소가 1차원 배열의 이름이자 시작 주솟값이었던 것처럼,

2차원 배열 역시 같은 이치입니다.

해당 코드는 2차원 배열에 있는 주소들이 각각 무엇을 의미하는지 보여드리려고 짜 본 코드입니다.

#include <stdio.h>

int main()
{
    int m[3][3] = {10, 20, 30, 40, 50, 60, 70, 80, 90};

    printf("m = %p\n", m); // 배열의 시작 주소

    printf("m[0] = %p\n", m[0]); // 1행의 시작 주소
    printf("m[1] = %p\n", m[1]); // 2행의 시작 주소
    printf("m[2] = %p\n", m[2]); // 3행의 시작 주소

    printf("&m[0][0] = %p\n", &m[0][0]);
    printf("&m[1][0] = %p\n", &m[1][0]);
    printf("&m[2][0] = %p\n", &m[2][0]);

    return 0;
}

이제 결과를 보도록 하겠습니다.

m = 0061FEFC
m[0] = 0061FEFC
m[1] = 0061FF08
m[2] = 0061FF14
&m[0][0] = 0061FEFC
&m[1][0] = 0061FF08
&m[2][0] = 0061FF14

각각의 코드들을 보면 그냥 m의 주소값과 m [0], m[0][0]의 주소값이 같은 것을 볼 수 있고,

이하 다른 것들도 1행과 1행 0열의 값, 2행과 2행 0열의 값이 같은 것을 볼 수 있습니다.

그래서 포인터가 가리키는 주솟값은 프로그래밍 언어에 따라서 첫 행 시작 주소나 열 시작 주소와 같음을 볼 수 있습니다.

그리고 각각의 주솟값은 기기마다 다 다르게 나오기 때문에 참고만 하시면 됩니다.

 

2. 다차원 배열의 이해

다차원 배열이라고 해서 그렇게 어려울 것은 없습니다.

제가 위에서 만든 배열 m은 3열 3행의 행렬, 다시 말하면 2차원 배열이었습니다.

이건 다시 말하면 3개의 배열 안에 또 다른 배열이 각각 3개씩 존재한다고 보면 이해가 쉽습니다.

다시 말하면 행들과 열들의 모임과 같습니다.

배열 m으로 예를 들자면,

m[0] ~ m [2] 안에 m [0][0] ~ m [2][2]라는 원소가 들어있다는 것이죠.

반응형

 

3. 다차원 배열에서의 포인터 연산

다차원 배열에서도 포인터에 대하여 덧셈과 뺄셈 연산을 할 수 있습니다.

여기서 우리가 연산을 한다는 것은 다차원 배열 m에다 덧셈과 뺄셈 연산을 한다고 할 수 있겠죠?

이 말이 무슨 뜻일까요?

일단 여기서 다루는 언어는 c언어이기 때문에 행우선 방식으로 저장을 했으니, 행을 위주로 보겠다는 뜻이 될 테니,

m+1이라 한다면 행을 한 칸 넘어간다는 뜻이 되겠죠?

얘를 들어서 m [0], 다시 말해서 제일 첫 행에서 m+1을 하게 되면 m [1]로 넘어간다는 의미가 됩니다.

 

4. 포인터를 이용한 다차원 배열 운행

우리가 배열에 있는 원소에 방문할 때 인덱스의 주소를 가지고서 방문을 했었습니다.

그러면 다차원 배열에서는 이를 가지고 어떻게 방문을 할까요?

이를 가장 잘 알아볼 수 있는 방법이 원소의 평균을 구하는 방법입니다.

다차원 배열에서 평균을 구하는 방법은 크게 두 가지입니다.

  • 특정한 행의 원소들의 평균을 구하는 경우
  • 전체 행렬의 원소들의 평균을 구하는 경우

 

4 - 1. 특정한 행의 원소들의 평균 구하기

이럴 경우에는 인덱스 대신에 포인터를 이용하는 방법이 편합니다.

그 이유는 어차피 같은 행에 속해있는 원소들은 저장한 순서대로 저장이 되어있기 때문입니다.

그래서 이럴 경우에는 포인터를 두 개를 이용하는 겁니다.

제일 첫 원소를 가리키는 포인터와 제일 마지막 원소를 가리키는 포인터를 말이죠.

그리고 반복문을 통하여 제일 첫 원소부터 마지막 원소까지 더하고,

마지막 원소를 가리키는 포인터를 지나가는 순간에 반복을 멈추게 하고, 그 수만큼 나눠주면 됩니다.

그럼 그 코드를 한 번 짜보도록 하겠습니다.

#include <stdio.h>

#define ROWS 4
#define COLS 3

double get_row_avg(int m[ROWS][COLS], int r);

int main()
{
    int m[ROWS][COLS] = {
        {10, 20, 30},
        {40, 50, 60},
        {70, 80, 90}, 
        {100, 110, 120}
        };

    int n;

    printf("행을 입력하세요 : ");
    scanf("%d", &n);

    printf("평균 = %lf\n", get_row_avg(m, n));

    return 0;
}

double get_row_avg(int m[ROWS][COLS], int r)
{
    int *p, *endp;
    double sum = 0.0;

    p = &m[r][0];
    endp = &m[r][COLS-1];

    while (p <= endp)
    {
        sum += *p++;
    }

    sum /= COLS; // 평균

    return sum;
}

이렇게 코드를 짜봤습니다.

그럼 결과를 보도록 하겠습니다.

행을 입력하세요 : 0
평균 = 20.000000

결과도 문제없이 나온 것을 확인할 수 있습니다.

 

4 - 2. 전체 행렬의 원소들의 평균을 구하는 경우

이럴 경우도 크게 어려운 것은 없습니다.

m [0][0]의 원소를 가리키는 포인터 하나, m [3][2]의 원소를 가리키는 포인터를 하나 지정을 해서

똑같이 가장 마지막 행렬의 원소를 지나는 순간에 반복을 종료시키고,

그걸 행과 열의 급으로 나눠주면 평균이 됩니다.

바로 코드를 짜보도록 하겠습니다.

#include <stdio.h>

#define ROWS 4
#define COLS 3

double get_total_avg(int m[][COLS]);

int main()
{
    int m[ROWS][COLS] = {
        {10, 20, 30},
        {40, 50, 60},
        {70, 80, 90}, 
        {100, 110, 120}
        };

    printf("평균 = %lf\n", get_total_avg(m));

    return 0;
}

double get_total_avg(int m[][COLS])
{
    int *p, *endp;
    double sum = 0.0;

    p = &m[0][0];
    endp = &m[ROWS-1][COLS-1];

    while (p <= endp)
    {
        sum += *p++;
    }

    sum /= ROWS * COLS; // 평균

    return sum;
}

이제 결과를 보도록 하겠습니다.

평균 = 65.000000

결과도 문제없이 잘 나오는 것을 볼 수 있습니다.

 

여기까지 다차원 배열과 포인터에 대하여 알아보았는데요,

다음 포스팅에서는 const 포인터와 volatile 포인터에 대하여 알아보도록 하겠습니다.

긴 글 읽어주신 독자분들께 진심으로 감사드립니다~

반응형

댓글