프로그래밍 C언어

다차원배열 (Multi-Dimensional Array)

게임첫걸음 2024. 8. 21. 10:12
#include <stdio.h>
#include <malloc.h>

void Array1DProcess(int _arr[3]);
void Array2DProcess(int _arr2D[][3]);
void Pointer2DProcess(int** _pArr2D)
{
	printf("_pArr2D Size: %d byte\n", sizeof(_pArr2D));
	printf("*(_pArr2D + 0) Size: %d byte\n", sizeof(*(_pArr2D + 0)));
	printf("*(*(_pArr2D + 0) + 0) Size: %d byte\n", sizeof(*(*(_pArr2D + 0) + 0)));

	printf("_pArr2D[0]: %p\n", _pArr2D[0]);
	printf("_pArr2D[1]: %p\n", _pArr2D[1]);
}

void main()
{
	// 다차원 배열 (Multi-Dimensional Array)
	// 2차원배열 [][]
	// int arr [Row][Column] [행]과 [열] ([가로], [세로])
	int arr[2][3] = { 11, 12, 13, 21, 22 };
	// int arr[][]에서 두 번째 []에는 값이 비면 안됨.
	// int arr[][3]이거 가능함. Row(행)이 비면 안됨.
	// 3차원 배열은 [][][], x, y, z값

	arr[1][1] = 55;
	// 값을 출력할 때 { 11, }, { 21, 22, } 출력하면 11, 0 ,0,  21, 55, 0  

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

	printf("arr Size: %d byte\n", sizeof(arr));
	printf("arr[0] Size: %d byte\n", sizeof(arr[0]));
	printf("arr[0][0] Size: %d byte\n", sizeof(arr[0][0]));

	printf("\n");

	printf("arr[0][4]: %d (%p)\n", arr[0][4], &arr[0][4]);
	// arr[0][4] == arr[1][1]과 같은 주소, 출력값을 가지고 있음.
	// 그 이유는 [0][4]는 [0][0]에서 4번 옮긴 값이고 [1][1]또한 4번 옮긴 값이기 때문이다.
	printf("arr: %p\n", arr);
	printf("arr[0]: %p\n", arr[0]);
	printf("arr[1]: %p\n", arr[1]);

	/////////////////////////////////////////////////////////////////////////////////////
	printf("\n");

	int arr1D[6] = { 11, 12, 13, 21, 22, 23 };
	int* pLine0 = &arr1D[0];	// 11
	int* pLine1 = &arr1D[3];	// 21

	int* arr2D[2] = { pLine0, pLine1 };
	int** pArr2D = arr2D;		

	printf("(pArr2D[1])[1]: %d\n", (pArr2D[1])[1]);				// 아래와 같다.
	printf("*(*(pArr2D + 1) + 1): %d\n", *(*(pArr2D + 1) + 1));	// 위와 같다
	
	/////////////////////////////////////////////////////////////////////////////////////
	printf("\n");

	int testArr[3] = { 1, 2, 3 };
	//int testArrEmpty[] = testArr;
	Array1DProcess(testArr);
	printf("testArr[0]: %d\n", testArr[0]);
	
	printf("\n");

	Array2DProcess(arr);

	////////////////////////////////////////////////////////////////////////////////////
	printf("\n");

	//동적배열 (Dynamic Array)
	int** pDArr2D = (int**)malloc(sizeof(int*) * 2);

	for (int row = 0; row < 2; ++row)
		pDArr2D[row] = (int*)malloc(sizeof(int) * 3);

	for (int row = 0; row < 2; ++row)
		for (int col = 0; col < 3; ++col)
			pDArr2D[row][col] = ((row + 1) * 10) + (col + 1);

	for (int row = 0; row < 2; ++row)
		for (int col = 0; col < 3; ++col)
			printf("*(*(pDArr2D + %d) + %d): %d\n", row, col, *(*(pDArr2D + row) + col));

	printf("\n");

	Pointer2DProcess(pDArr2D);

	for (int row = 0; row < 2; ++row)
	{
		if (*(pDArr2D + row) != NULL)
		{
			free(*(pDArr2D + row));
			*(pDArr2D + row) = NULL;
		}
	}

	if (pDArr2D)
	{
		free(pDArr2D);
		pDArr2D = NULL;
	}
}

void Array1DProcess(int _arr[3])
{
	printf("_arr Size: %d byte\n", sizeof(_arr));
	_arr[0] = 100;

	for (int i = 0; i < 3; ++i)
		printf("_arr[%d]: %d\n", i, _arr[i]);
}

void Array2DProcess(int _arr2D[][3])
{
	printf("_arr2D Size: %d byte\n", sizeof(_arr2D));
	printf("_arr2D[0] Size: %d byte\n", sizeof(_arr2D[0]));

	for (int row = 0; row < 2; ++row)
	{
		for (int col = 0; col < 4; ++col)
		{
			printf("_arr[%d][%d]: %d\n", row, col, _arr2D[row][col]);
		}
	}
}

오늘은 다차원 배열 중 2차원 배열에 대해 알아보겠습니다.

다차원배열은 [ ] 대괄호의 갯수에 따라 이름이 결정됩니다. [ ][ ] 면 2차원, [ ][ ][ ] 면 3차원이라고 생각하시면 됩니다. 그리고 다차원배열을 하기 전에 우리가 이해하기 쉽도록 그림으로 표시하는 다차원 배열과 실제 컴퓨터가 처리하는 다차원 배열의 차이를 그림판으로 설명드리겠습니다.

2차원 배열의 Row(행)와 Column(열)
다차원 배열의 이해

 arr[2][3]이라는 2차원 배열에 대한 설명입니다. 위의 코드에서 이와 관련된 출력값을 다룬 코드를 살펴보겠습니다.

int arr[2][3] = { 11, 12, 13, 21, 22 };                                                             // 없는 요소값은 자동 0으로 자동 초기화

arr[1][1] = 55;                                                                                              // 5번째 요소값을 55로 초기화
// 값을 출력할 때 { 11, }, { 21, 22, } 출력하면 11, 0 ,0,  21, 55, 0                // 없는 요소값은 0으로 자동 초기화

// row (행), col (열)로 col은 비워도 되지만, row는 빈칸을 넣으면 안된다.
for (int row = 0; row < 2; ++row)                                                                 // 배열이 잘 출력되는지에 대한 반복문
{
for (int col = 0; col < 3; ++col)                                                                    
{
printf("arr[%d][%d]: %d (%p)\n", row, col, arr[row][col], &arr[row][col]);
}
}
printf("arr Size: %d byte\n", sizeof(arr));                                                     // arr[2][3] 배열 크기 출력문
printf("arr[0] Size: %d byte\n", sizeof(arr[0]));                                             // arr[0] 배열 크기 출력문
printf("arr[0][0] Size: %d byte\n", sizeof(arr[0][0]));                                     // arr[0][0] 배열 크기 출력문

printf("\n");

printf("arr[0][4]: %d (%p)\n", arr[0][4], &arr[0][4]);                                       // arr[0][4]의 값 출력문

 여기서 배열의 크기에 대한 설명, 출력할 배열의 col값보다 높은 숫자를 넣어도 가능한 이유 두 가지를 설명하겠습니다.

  1. arr[2][3] 배열 크기는 int 자료형의 기본 크기인 4byte가 6개 요소가 존재하여 24 byte가 출력됩니다.
  2. arr[0] 배열 크기는 arr[0][0] ~ arr[0][2]까지의 크기로 4byte에 3개 요소가 존재하여 12 byte가 출력됩니다.
  3. arr[0][0] 배열 크기는 arr배열의 첫 번째 요소에 대한 크기입니다. 따라서 4 byte가 출력됩니다.
  4. arr[0][4]는 arr[2][3]에서 col(열)의 값이 넘어버리는 상황입니다. 그러나, 오류가 나오지않고 5번째 요소인 arr[1][1]의 값, 55가 출력됩니다. 그 이유는 그림판에서의 예시를 보시면 이해하기 쉽습니다. 우리가 흔히 행과 열로 표기하는 2차원 배열을 실제 컴퓨터에서는 1행에 모든 열로 나열하는 방식으로 다룹니다. 그에 따라 5번째 요소인 [1][1]의 주소와 값을 가지며 출력됩니다.

<포인터 배열> (Pointer Array)

int arr1D[6] = { 11, 12, 13, 21, 22, 23 };                             // arr1D라는 6크기의 1차원 배열
int* pLine0 = &arr1D[0]; // 11                                             // arr1D의 첫 번째 주소값을 가지는 pLine0 포인터 변수
int* pLine1 = &arr1D[3]; // 21                                             // arr1D의 네 번째 주소값을 가지는 pLine1 포인터 변수

int* arr2D[2] = { pLine0, pLine1 };                                      // pLine0, 1 포인터 변수를 배열로 가진 arr2D라는 포인터 배열 
int** pArr2D = arr2D;                                                         // arr2D의 첫 번째 요소 pLine0 이중 포인터 pArr2D

  • arr2D[2] 포인터 배열에 pLine0, pLine1를 넣음. 
  • Arr2D 이중 포인터에는 arr2D의 첫 번째 요소값 pLine0을 부여합니다. 여기서 이중 포인터는 2차원 배열에서 사용됩니다.

printf("(pArr2D[1])[1]: %d\n", (pArr2D[1])[1]);                     // pArr2D[1][1]: 22
printf("*(*(pArr2D + 1) + 1): %d\n", *(*(pArr2D + 1) + 1));  // *(*(pArr2D + 1) + 1): 22 

 주석은 출력값입니다.

  • pArr2D[1]의 [1]은 arr2D[1]을 가리키며 pLine1의 arr1D[3] 21 다음 22가 값으로 출력
  • *(pArr2D + 1) + 1에서 *(pArr2D+1)은 arr2D[1]의 위치값과 같습니다. 때문에 pLine1입니다. 거기다 +1의 포인터, 위치값이기 때문에 arr2D[1][1]이 됩니다. 그래서 출력값은 22가 됩니다.

void main()

{

int testArr[3] = { 1, 2, 3 };                                         // 3크기를 가진 1차원 배열 testArr 배열
Array1DProcess(testArr);                                       // Array1DProcess함수의 매개변수에 testArr 배열 대입
printf("testArr[0]: %d\n", testArr[0]);                        // testArr[0]: 100

}

 

void Array1DProcess(int _arr[3])                             // 3크기를 가진 1차원 배열 _arr을 매개변수로 삼은 함수 (포인터)
{
printf("_arr Size: %d byte\n", sizeof(_arr));              // _arr Size: 8 byte
_arr[0] = 100;                                                           // _arr 배열 첫 번째 요소에 100으로 초기화;

for (int i = 0; i < 3; ++i)                                             // 반복문
printf("_arr[%d]: %d\n", i, _arr[i]);                            // _arr[0]: 100 _arr[1]: 2 _arr[2]: 3
}

 매크로를 이용하여 Array1DProcess 함수를 이용한 코드입니다. 관련 항목들 정리하겠습니다.

  • 함수의 매개변수가 배열이라면, 그 매개변수는 포인터로 치환됩니다. 그래서 sizeof를 한다면 8byte입니다.
  • 첫 번째 요소의 값을 100으로 초기화했기 때문에 testArr[0]: 100, _arr[0]: 100, _arr[1]: 2, _arr[2]: 3이 출력됩니다.

<동적 배열> (Dynamic Array)

 

#include <malloc.h>

 

void main()
{
   int** pDArr2D = (int**)malloc(sizeof(int*) * 2);              //2차원 배열의 pDArr2D 크기 설정

   for (int row = 0; row < 2; ++row)                                  // 2차원 배열의 각 행에 int * 3의 메모리를 할당
      pDArr2D[row] = (int*)malloc(sizeof(int) * 3);

   for (int row = 0; row < 2; ++row)                                  // 행 2줄
      for (int col = 0; col < 3; ++col)                                  // 열 3줄
         pDArr2D[row][col] = ((row + 1) * 10) + (col + 1);  // 배열에 값 할당 행번호 + 1 * 10 + 열번호 + 1

   for (int row = 0; row < 2; ++row)            // 배열의 모든 요소를 출력. *(*(pDArr2D + row) + col) = pDArr2D[row][col]
      for (int col = 0; col < 3; ++col)       
         printf("*(*(pDArr2D + %d) + %d): %d\n", row, col, *(*(pDArr2D + row) + col));

printf("\n");

Pointer2DProcess(pDArr2D);                  // pDArr2D 매개 포인터 변수를 가진 2DProcess 함수

       for (int row = 0; row < 2; ++row)       // 행 2줄
       {
               if (*(pDArr2D + row) != NULL)  // pDArr2D에서 row만큼 떨어진 위치의 포인터 pDArr2D[row]       

               {                                                // NULL이 아니라면
                     free(*(pDArr2D + row));      // pDArr2D + row 포인터의 동적 할당된 메모리를 해제
                     *(pDArr2D + row) = NULL; // pDArr2D + row 메모리 해제 후 NULL로 유효하지 않은 상태
               }
        }

        if (pDArr2D)                                      // pDArr2D라면
        {
              free(pDArr2D);                            // pDArr2D에 동적 할당된 메모리를 해제
              pDArr2D = NULL;                       // pDArr2D 메모리 해제 후 NULL로 유효하지 않은 상태
        }
}

void Array2DProcess(int _arr2D[][3])        // Array2DProcess 함수 int_arr2D[ ][3] 2차원 배열 매개 변수
{
        printf("_arr2D Size: %d byte\n", sizeof(_arr2D));          //_arr2D Size: 12byte (int byte * 3)
        printf("_arr2D[0] Size: %d byte\n", sizeof(_arr2D[0]));  //_arr2D[0] Size: 12byte (int byte * 3)

        for (int row = 0; row < 2; ++row)                                    // 행 2줄
        {
              for (int col = 0; col < 4; ++col)                                 // 열 4줄
              {
                    printf("_arr[%d][%d]: %d\n", row, col, _arr2D[row][col]);   // _arr[0~1][0~3]: 값
              }
        }
}

 

위의 코드 중 Array2DProcess 함수에서 _arr2D Size와 _arr2D[0] Size가 같은 이유는 지난 번 배열에서 그림판으로 설명했던 것이 적용되는데 _arr2D[ ][ ]은 행이 0으로 처리됩니다. 즉 3개의 요소를 가지는 배열이라는 뜻이기 때문에 전체 size값은 int의 4byte에 * 3를 하여 12byte가 되고, _arr2D[0]은 한 줄에 포함된 모든 요소들의 크기 합을 나타냅니다. 그래서 똑같이 12byte가 출력됩니다. 

 

'프로그래밍 C언어' 카테고리의 다른 글

큐 (Queue)  (0) 2024.08.21
스택 (Stack)  (0) 2024.08.21
상수 열거형 (Enumeration)  (0) 2024.08.20
공용체 (Union)  (0) 2024.08.20
구조체 (Structure)  (0) 2024.08.20