Skip to main content

Command Palette

Search for a command to run...

2차원 배열 이름의 포인터 형

Published
4 min readView as Markdown

2차원 배열 이름이 가리키는 것들

#include <stdio.h>

int main(void)
{
    int arr2d[3][3];
    printf("%d\n", arr2d);
    printf("%d\n", arr2d[0]);
    printf("%d\n\n", &arr2d[0][0]);

    printf("%d\n", arr2d[1]);
    printf("%d\n\n", &arr2d[1][0]);

    printf("%d\n", arr2d[2]);
    printf("%d\n\n", &arr2d[2][0]);

    printf("sizeof(arr2d): %lu\n", sizeof(arr2d));
    printf("sizeof(arr2d[0]): %lu\n", sizeof(arr2d[0]));
    printf("sizeof(arr2d[1]): %lu\n", sizeof(arr2d[1]));
    printf("sizeof(arr2d[2]): %lu\n", sizeof(arr2d[2]));
    return 0;
}

/* 결과
1795043140
1795043140
1795043140

1795043152
1795043152

1795043164
1795043164

sizeof(arr2d): 36
sizeof(arr2d[0]): 12
sizeof(arr2d[1]): 12
sizeof(arr2d[2]): 12

배열 이름 arr2d를 대상으로 sizeof 연산을 하는 경우 배열 전체의 크기를 반환
arr2d[0], arr2d[1], arr2d[2]를 대상으로 sizeof 연산을 하는 경우 각 행의 크기를 반환

arr2d는 첫 번째 요소를 가리키면서 배열 전체를 의마한다. 하지만, arr2d[0]은 첫 번째 요소를 가리키되 1행만을 의미한다.
*/

배열 이름 기반의 포인터 연산: 배열 이름에 1을 더한 결과는?

두 포인터의 포인터 형이 같다면, 두 포인터를 대상으로 하는 증가 및 감소 연산의 결과로 증가 및 감소하는 값의 크기는 동일하다.

#include <stdio.h>

int main(void)
{
    int arr1[3][2];
    int arr2[2][3];

    printf("arr1: %p\n", arr1);
    printf("arr1+1: %p\n", arr1+1);
    printf("arr1+2: %p\n", arr1+2);

    printf("arr2: %p\n", arr2);
    printf("arr2+1: %p\n", arr2+1);
    return 0;
}

/* 결과
arr1: 0x16db4ef50
arr1+1: 0x16db4ef58
arr1+2: 0x16db4ef60
arr2: 0x16db4ef38
arr2+1: 0x16db4ef44

2차원 배열 이름을 대상으로 증감 연산을 할 경우, 연산 결과는 각 행의 첫 번째 요소의 주소 값이 됌
때문에 자료형이 동일하더라도 배열의 가로길이가 다르다면, 포인터 형이 다르다.
*/

최종 결론! 2차원 배열 이름의 포인터 형

2차원 배열 이름의 포인터 형에는 두 가지 정보가 담겨야 함.

  • 가리키는 대상은 무엇인가

  • 배열 이름(포인터)를 대상으로 값을 1 증가 및 감소 시 실제로는 얼마가 증가 및 감소하는가

int arr[3][4];
// arr은 가리키는 대상이 int형 변수이고, 포인터 연산 시 sizeof(int) x 4의 크기 단위로 증가 및 감소하는 포인터 형이다.

포인터 변수의 선언

int (*ptr) [4];
/* 
int: int형 변수를 가리키는 포인터!
(*ptr): ptr은 포인터!
[4]: 포인터 연산 시 4칸 씩 건너뛰는 포인터!
이러한 변수는 2차원 배열을 가리키는 용도로만 사용되기 때문에 '배열 포인터 변수'라 한다.
*/
#include <stdio.h>

int main(void)
{
    int arr1[2][2]={
        {1, 2}, {3, 4}
    };
    int arr2[3][2]={
        {1, 2}, {3, 4}, {5, 6}
    };
    int arr3[4][2]={
        {1, 2}, {3, 4}, {5, 6}, {7, 8}
    };

    int (*ptr) [2];
    int i;

    ptr=arr1;
    printf("** Show 2,2 arr1 **\n");
    for(i=0 ; i<2 ; i++)
        printf("%d %d\n", ptr[i][0], ptr[i][1]);

    ptr=arr2;
    printf("** Show 3,2 arr2 **\n");
    for(i=0 ; i<3 ; i++)
        printf("%d %d\n", ptr[i][0], ptr[i][1]);

    ptr=arr3;
    printf("** Show 4,2 arr3 **\n");
    for(i=0 ; i<4 ; i++)
        printf("%d %d\n", ptr[i][0], ptr[i][1]);
    return 0;
}

/* 결과
** Show 2,2 arr1 **
1 2
3 4
** Show 3,2 arr2 **
1 2
3 4
5 6
** Show 4,2 arr3 **
1 2
3 4
5 6
7 8
*/

2차원 배열 이름의 특성과 주의 사항

포인터 배열과 배열 포인터의 차이

#include <stdio.h>

int main(void)
{
    int num1=10, num2=20, num3=30, num4=40;
    int arr2d[2][4]={1, 2, 3, 4, 5, 6, 7, 8};
    int i, j;

    int * whoA[4]={&num1, &num2, &num3, &num4};     // 포인터 배열
    int (*whoB)[4]=arr2d;       // 배열 포인터

    printf("%d %d %d %d\n", *whoA[0], *whoA[1], *whoA[2], *whoA[3]);
    for(i=0 ; i<2 ; i++)
    {
        for(j=0 ; j<4 ; j++)
            printf("%d ", whoB[i][j]);
        printf("\n");
    }
    return 0;
}

/* 결과
10 20 30 40
1 2 3 4 
5 6 7 8 
*/

2차원 배열을 함수의 인자로 전달하기

int main(void)
{
    int arr1[2][7];
    double arr2[4][5];
    SimpleFunc(arr1, arr2);
    . . . .
}

/* 
arr1의 주소 값을 전달 받을 수 있는 매개 변수의 이름을 parr1,
arr2의 주소 값을 전달 받을 수 있는 매개 변수의 이름을 parr2
*/

int (*parr1)[7]
double (*parr2)[5]
// 따라서 SimpleFunc 함수가 void 형이라고 가정하면 아래처럼 선언
void SimpleFunc(int (*parr1)[7], double (*parr2)[5]) { . . . . }
#include <stdio.h>

void ShowArr2DStyle(int (*arr)[4], int column)      // 배열 요소 전체 출력
{
    int i, j;
    for(i=0 ; i<column ; i++)
    {
        for(j=0 ; j<4 ; j++)
            printf("%d ", arr[i][j]);
        printf("\n");
    }
    printf("\n");
}

int Sum2DArr(int arr[][4], int column)      // 배열 요소의 합 반환
{
    int i, j, sum=0;
    for(i=0 ; i<column ; i++)
        for(j=0 ; j<4 ; j++)
            sum += arr[i][j];
    return sum;
}

int main(void)
{
    int arr1[2][4]={1, 2, 3, 4, 5, 6, 7, 8};
    int arr2[3][4]={1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 5, 5};

    ShowArr2DStyle(arr1, sizeof(arr1)/sizeof(arr1[0]));
    ShowArr2DStyle(arr2, sizeof(arr2)/sizeof(arr2[0]));

    printf("arr1의 합: %d\n", Sum2DArr(arr1, sizeof(arr1)/sizeof(arr1[0])));
    printf("arr2의 합: %d\n", Sum2DArr(arr2, sizeof(arr2)/sizeof(arr2[0])));
    return 0;
}

/* 결과
1 2 3 4 
5 6 7 8 

1 1 1 1 
3 3 3 3 
5 5 5 5 

arr1의 합: 36
arr2의 합: 36

두 함수의 두 번째 매개변수를 배열의 세로길이를 전달받는다. 따라서 가로 길이가 4인 int형 2차원 배열이라면 세로 길이에 상관 없이 인자로 전달이 가능
sizeof(arr1) / sizeof(arr1[0])
sizeof(arr1) / sizeof(arr1[0])
배열의 전체 크기 / 배열의 가로 크기 = 배열의 세로 길이
*/

2차원 배열에서도 arr[i]*(arr+i)는 같다.

// 1차원 배열
arr[i] == *(arr+i)  // arr[i]는 *(arr+i)와 같다.
// 이 식은 다차원 배열에서도 성립
#include <stdio.h>

int main(void)
{
    int a[3][2]={{1, 2}, {3, 4}, {5, 6}};

    printf("a[0]: %p\n", a[0]);
    printf("*(a+0): %p\n", *(a+0));

    printf("a[1]: %p\n", a[1]);
    printf("*(a+1): %p\n", *(a+1));

    printf("a[2]: %p\n", a[2]);
    printf("*(a+2): %p\n", *(a+2));

    printf("%d, %d\n", a[2][1], (*(a+2))[1]);
    printf("%d, %d\n", a[2][1], *(a[2]+1));
    printf("%d, %d\n", a[2][1], *(*(a+2)+1));
    return 0;
}

/* 결과
Chapter18/"2DArrAccessType
a[0]: 0x16f092f40
*(a+0): 0x16f092f40
a[1]: 0x16f092f48
*(a+1): 0x16f092f48
a[2]: 0x16f092f50
*(a+2): 0x16f092f50
6, 6
6, 6
6, 6
*/

More from this blog

선행처리기와 매크로

선행처리는 컴파일 이전의 처리를 의미 선행처리는 선행처리기에 의해서, 컴파일은 컴파일러에 의해서, 링크는 링커에 의해서 진행 소스파일 → 선행처리기 → 선행처리 거친 소스파일 → 컴파일러 → 오브젝트 파일 → 링커 → 실행파일 컴파일 과정을 거치게 되면 바이너리 데이터로 이루어진 오브젝트 파일이 생성 선행처리기가 하는 일 삽입해 놓은 선행처리 명령문대로 소스코드의 일부를 수정 여기서 수정이란, 단순 치환의 형태 /* 간단한 선행처리 명령문 */...

Oct 10, 20246 min read

C 언어의 메모리 구조

프로그램을 실행하면 해당 프로그램의 실행을 위한 메모리 공간이 운영체제에 의해서 미리 마련 그리고 바로 이 메모리 공간 내에서 변수가 선언되고, 문자열이 선언되는 것 메모리의 구성 프로그램 실행 시 운영체제에 의해서 마련되는 메모리의 구조는 네 개의 영역으로 구분 코드 영역 / 데이터 영역 / 힙 영역 / 스택 영역 메모리 공간을 나눠놓은 이유는 유사한 성향의 데이터를 묶어서 저장을 하면, 관리가 용이해지고 메모리의 접근속도가 향상 메모리 영역...

Oct 9, 20246 min read

파일과 스트림(Stream), 그리고 기본적인 파일의 입출력

저기 저 파일에 저장되어 있는 데이터를 읽고 싶어요. 프로그램 상에서 파일에 저장되어 있는 데이터를 참조하길 원한다고(읽기 원한다고) 가정했을 때, 우리가 구현한 프로그램과 참조할 데이터가 저장되어 있는 파일 사이에 데이터가 이동할 수 있는 다리를 놓는 일을 먼저 해야 한다. 이러한 데이터 이동의 경로가 되는 다리를 가리켜 ‘스트림(stream)’이라 함. 스트림이라는 것은 운영체제에 의해서 형성되는 소프트웨어적인 상태를 의미하는 것 따라서, ...

Oct 8, 20249 min read

JinGood

31 posts