본문 바로가기
Language&Framework&Etc/C

다차원 배열(16-1) 다차원 배열의 이해와 활용

by 머리올리자 2020. 12. 9.

다차원 배열 : 2차원 이상의 배열

 

2차원, 3차원 배열? OK! 4차원, 5차원 배열? NO!

2차원 배열 : 평면 구조

3차원 배열 : 직육면체 구조

4차원, 5차원 : ....

 

1차원 배열

길이가 10인 1차원 int형 배열

int arronedim[10];

 

2차원 배열

가로, 세로의 길이가 5인 2차원 int형 배열

int arrtwodim[5][5];

 

3차원 배열

가로, 세로, 높이의 길이가 3인 3차원 int형 배열

int arrthreedim[3][3][3];

 

다차원 배열을 대표하는 2차원 배열의 선언

int arr1[3][4]; // 세로가 3, 가로가 4인 int형 2차원 배열
int arr2[2][6]; // 세로가 2, 가로가 6인 int형 2차원 배열

int arr1[3][4]의 형태
int arr2[2][6]의 형태

 

배열요소 안에 삽입된 두 개의 숫자는 각각의 요소에 접근할 때 사용하게 되는 인덱스 값

 

배열 이름이 arr이고 배열요소의 자료형이 TYPE이라 할 때, 2차원 배열의 선언형태는 다음과 같다

 

TYPE arr[세로길이][가로길이];

 

2차원 배열의 크기

#include <stdio.h>

int main(void)
{
	int arr1[3][4];
	int arr2[7][9];

	printf("세로3, 가로4: %d \n", sizeof(arr1));
	printf("세로7, 가로9: %d \n", sizeof(arr2));
}

 

3(세로) x 4(가로) x int 4 = 48 바이트

7(세로) x 9(가로) x int 4 = 252 바이트

 

2차원 배열요소의 접근

int arr[3][3];

0으로 초기화 되었다고 가정

 

이 문장이 실행되면

arr[0][0]=1;

 

 

다음 문장이 실행되면

arr[0][1]=2;

arr[2][1]=5;

이름이 arr인 int형 배열을 대상으로 세로 N번째 위치(1부터 시작해서 N번째), 그리고 가로 M번째 위치(1부터 시작해서 M번째)에 저장된 값을 변경 및 참조하는 방법

 

arr[N-1][M-1] = 20;           // 세로 N, 가로 M의 위치에 정수 20을 저장
printf("%d", arr[N-1][M-1])   // 세로 N, 가로 M의 위치에 저장된 값 출력

층별로 두 가구가 사는 4층짜리 빌라의 가구별 거주인원 수 예시

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


int main(void)
{
	int villa[4][2];
	int popu;

	/* 가구별 거주인원 입력 받기 */
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			printf("%d층 %d호 인구수 : ", i + 1, j + 1);
			scanf("%d", &villa[i][j]);
		}
	}

	/* 빌라의 층별 인구수 출력하기*/

	for (int i = 0; i < 4; i++)
	{
		popu = 0;
		for (int j = 0; j < 2; j++)
		{
			popu += villa[i][j];
		}
		printf("%d층 인구수 : %d \n", i + 1, popu);
	}

}

  • 위 예제는 1차원 배열로도 구현이 가능
  • 그러나 이는 프로그램을 복잡하게 만들뿐임
  • 2차원적 모델의 문제를 해결하기 위해서는 2차원 배열을 선언하는 것이 바람직

2차원 배열의 메모리상 할당의 형태

2차원 배열은 물리적으로도 2차원 형태로 존재?

 

  • 우리가 사용하는 메모리의 주소 값은 다음과 같이 1차원적 구조.
  • 0x1001번지, 0x1001번지, 0x1003번지, 0x1004번지, 0x1005번지....

2차원적 구조였다면 메모리의 주소 값은 다음의 형태

  • 0x12-0x24번지, 0x12-0x25번지, 0x12-0x26번지, 0x12-0x27번지......
  • 0x13-0x24번지, 0x13-0x25번지, 0x13-0x26번지, 0x13-0x27번지......
  • 0x14-0x24번지, 0x14-0x25번지, 0x14-0x26번지, 0x14-0x27번지......

따라서 2차원 배열도 메모리상에는 1차원의 형태로 존재.

 

세로 길이가 3이고 가로 길이가 2인 int형 배열을 선언하면 다음과 같은 구조로 메모리상에 존재

 

 

실제로 위 그림의 형태로 배열이 할당되는지 확인. 2차원 배열요소의 주소 값을 출력

#include <stdio.h>


int main(void)
{
	int arr[3][2];

	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			printf("%p\n", &arr[i][j]);
		}
	}
}

 

2차원 배열 선언과 동시에 초기화

2차원 배열도 선언과 동시에 초기화 가능

 

가장 일반적인 초기화 방법

#include <stdio.h>

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

 

행 단위로 초기화할 값들을 별도의 중괄호로 명시해야 한다.

물론 모든 배열요소를 초기화해야 하는 것은 아니다.

일부 요소에 대해서는 초기화를 생략할 수 있다.

이렇게 해서 비게 되는 공간은 1차원 배열과 마찬가지로 0으로 초기화된다.

 

#include <stdio.h>

int main(void)
{
	int arr[3][3] = {
		{1},
		{4, 5},
		{7, 8, 9}
	};
	printf("%d\n", arr[0][1]);
}

초기화가 잘 된 것을 확인

위 초기화는 아래와 같음

#include <stdio.h>

int main(void)
{
	int arr[3][3] = {
		{1, 0, 0},
		{4, 5, 0},
		{7, 8, 9}
	};
	printf("%d\n", arr[0][1]);
}

 

초기화 했을 때 부족한 영역은 0으로 초기화된다.

한줄에 선언하는 것도 가능

int arr[3][3] = {1, 2, 3, 4, 5, 6, 7};

다음의 배열선언과 동일한 결과로 이어짐

int arr[3][3] = {1, 2, 3, 4, 5, 6, 7, 0, 0};

 

초기화 관련내용 확인 예제

#include <stdio.h>


int main(void)
{
	/* 2차원 배열 초기화의 예 1*/
	
	int arr1[3][3] = {
		{1, 2, 3},
		{4, 5, 6},
		{7, 8, 9}
	};

	/* 2차원 배열 초기화의 예 2*/

	int arr2[3][3] = {
		{1},
		{4, 5},
		{7, 8, 9}
	};

	/* 2차원 배열 초기화의 예 3*/

	int arr3[3][3] = {1, 2, 3, 4, 5, 6, 7};

	/* 출력 1 */
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
			printf("%d ", arr1[i][j]);
		printf("\n");
	}
	printf("\n");

	/* 출력 2 */
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
			printf("%d ", arr2[i][j]);
		printf("\n");
	}
	printf("\n");

	/* 출력 3 */
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
			printf("%d ", arr3[i][j]);
		printf("\n");
	}
	printf("\n");

}

 

배열의 크기를 알려주지 않고 초기화하기

1차원 배열선언 시, 초기화 리스트가 존재한다면, 배열의 길이를 명시하지 않아도 됐음

2차원 배열도 마찬가지.

2차원 배열도 초기화 리스트가 존재한다면, 그 길이를 명시하지 않아도 된다.

 

단, 제약사항이 있다.

 

int arr[][] = {1, 2, 3, 4, 5, 6, 7, 8};

위 문장을 본다면 배열의 가로와 세로의 길이를 어떻게 결정?

 

여기에는 여러 가지의 경우의 수가 존재

(2 x 4도 되고 4 x 2도 됨)

(1 x 8도 되고 8 x 1도 됨)

 

따라서 컴파일러는 "하나는 가르쳐줘야 된다" 라고 한다

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

 

위와 같이 설정하면 된다

arr1의 경우 가로의 길이가 4이니, 세로의 길이는 2가 될 것이고

arr2의 경우 가로의 길이가 2이니, 세로의 길이는 4가 될 것이다.

 

"2차원 배열을 선언과 동시에 초기화하는 경우에는 배열의 세로길이만 생략이 가능하다."

 

참고 : [윤성우 열혈 C 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.