Unit 37. 2차원 배열 사용하기
2차원 배열은 다음과 같이 가로 x 세로 의 형태로 되어 있는 평면 구조이며 행과 열 모두 0부터 시작한다.
37.1 2차원 배열을 선언하고 요소에 접근하기
2차원 배열은 [](대괄호)를 두번 사용하여 선언하며 첫 번째 대괄호는 세로크기, 두 번째 대괄호는 가로 크기를 지정한다.
2차원 배열을 선언하면서 초기화 하려면 { }(중괄호)를 사용하는데, 다음과 같이 가로 요소들을 먼저 묶고, 가로 줄을 세로 크기만큼 다시 묶는다.
int numArr[3][4] = {
{ 가로 요소 4개 },
{ 가로 요소 4개 },
{ 가로 요소 4개 }
};
{ }를 사용라여 배열에 값을 할당하는 방법은 배열을 선언할 때만 사용할 수 있으며 이미 선언한 배열에는 사용할 수 없다.
2차원 배열의 요소에 접근하려면 배열 뒤에 [ ](대괄호)를 두 번 사용하여 안에 세로와 가로 인덱스를 지정하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include < stdio.h>
int main()
{
int numArr[3 ][4 ] = {
{1 ,2 ,3 ,4 },
{5 ,6 ,7 ,8 },
{9 ,10 ,11 ,12 }
};
printf ("%d\n" , numArr[0 ][0 ]);
printf ("%d\n" , numArr[1 ][2 ]);
printf ("%d\n" , numArr[2 ][0 ]);
printf ("%d\n" , numArr[2 ][3 ]);
return 0 ;
}
cs
세로크기가 3, 가로크기가 4인 int형 2차원 배열을 선언하고, 값을 초기화 했다.
2차원 배열도 가로, 세로 인덱스 모두 0부터 시작한다.
배열을 초기화 할때 가로 요소를 중괄호로 묶지 않아도 되지만 알아보기가 힘들어 잘 사용하진 않는다.
37.2 2차원 배열을 초기화하기
2차원 배열은 다음 코드로 모든 요소를 0으로 초기화 할 수 있다.
int numArr[3][4] = { 0, };
37.3 2차원 배열의 요소에 값 할당하기
다음 코드와 같이 값을 초기화 하지 않은 배열의 요소에 값을 할당할 수 있다.
int numArr[3][4];
numArr[0][1] = 5;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include < stdio.h>
int main()
{
int numArr[3 ][4 ];
numArr[0 ][0 ] = 11 ;
numArr[0 ][1 ] = 22 ;
numArr[0 ][2 ] = 33 ;
numArr[0 ][3 ] = 44 ;
numArr[1 ][0 ] = 55 ;
numArr[1 ][1 ] = 66 ;
numArr[1 ][2 ] = 77 ;
numArr[1 ][3 ] = 88 ;
numArr[2 ][0 ] = 99 ;
numArr[2 ][1 ] = 110 ;
numArr[2 ][2 ] = 121 ;
numArr[2 ][3 ] = 132 ;
printf ("%d\n" , numArr[- 1 ][- 1 ]);
printf ("%d\n" , numArr[0 ][4 ]);
printf ("%d\n" , numArr[4 ][0 ]);
printf ("%d\n" , numArr[5 ][5 ]);
return 0 ;
}
cs
2차원 배열도 범위를 벗어난 인덱스에 접근하여 출력하면 쓰레기값이 출력된다. 배열의 범위를 벗어난 인덱스에 접근하면 배열이 아닌 다른 메모리 공간에 접근하게 된다. 또한 [0][4]처럼 가로 인덱스만 범위를 벗어나도록 지정하면 그 다음 세로 인덱스 요소인 [1][0]에 접근된다.
37.4 2차원 배열의 크기 구하기
2차원 배열의 전체공간, 가로, 세로 요소의 개수는 다음과 같이 sizeof 연산자를 이용하여 구할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include < stdio.h>
int main()
{
int numArr[3 ][4 ] = {
{ 11 , 22 , 33 , 44 },
{ 55 , 66 , 77 , 88 },
{ 99 , 110 , 121 , 132 }
};
printf ("%d\n" , sizeof (numArr));
int col = sizeof (numArr[0 ]) / sizeof (int );
int row = sizeof (numArr) / sizeof (numArr[0 ]);
printf ("%d\n" , col);
printf ("%d\n" , row);
return 0 ;
}
cs
sizeof로 2차원 배열의 크기를 구하면 전체 공간을 구할 수 있다. 위에서는 4바이트 요소가 8개 있으므로 48이 나온다.
가로의 요소 개수는 가로 한 줄의 크기를 요소 자료형의 크기로 나누면 된다.
세로의 요소 개수는 배열의 전체 공간을 가로 한 줄의 크기로 나누면 된다.
37.5 반복문으로 2차원 배열의 요소를 모두 출력하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include < stdio.h>
int main()
{
int numArr[3 ][4 ] = {
{ 11 , 22 , 33 , 44 },
{ 55 , 66 , 77 , 88 },
{ 99 , 110 , 121 , 132 }
};
int col = sizeof (numArr[0 ]) / sizeof (int );
int row = sizeof (numArr) / sizeof (numArr[0 ]);
for (int i = 0 ; i < row; i+ + )
{
for (int j = 0 ; j < col; j+ + )
{
printf ("%d " , numArr[i][j]);
}
printf ("\n" );
}
return 0 ;
}
cs
배열의 세로와 가로 요소의 개수를 구하고, 2중 for문으로 세로부터 반복하고, 가로를 반복한다. 2차원 배열의 인덱스로는 세로에는 바깥쪽 반복문의 i를, 가로에는 안쪽 반복문의 j를 넣어 배열의 요소를 순서대로 접근하여 출력할 수 있다. 안쪽 반복문이 끝나면 개행문자를 출력함으로 세로줄이 바뀔때 줄을 바꿔 출력한다.
다음 코드와 같이 역순으로도 출력할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include < stdio.h>
int main()
{
int numArr[3 ][4 ] = {
{ 11 , 22 , 33 , 44 },
{ 55 , 66 , 77 , 88 },
{ 99 , 110 , 121 , 132 }
};
int col = sizeof (numArr[0 ]) / sizeof (int );
int row = sizeof (numArr) / sizeof (numArr[0 ]);
for (int i = row - 1 ; i > = 0 ; i- - )
{
for (int j = col- 1 ; j > = 0 ; j- - )
{
printf ("%d " , numArr[i][j]);
}
printf ("\n" );
}
return 0 ;
}
cs
세로와 가로의 마지막 인덱스부터 0까지 인덱스를 1씩 감소시키면서 출력하면 된다.
37.6 2차원 배열을 포인터에넣기
2차원 배열을 포인터에 담으려면 가로의 크기를 알아야 한다. 다음과 같이 포인터를 선언할 때 *과 포인터 이름을 괄호로 묶은 뒤 []에 가로 크기를 지정하면 된다.
int (*numPtr)[4];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include < stdio.h>
int main()
{
int numArr[3 ][4 ] = {
{ 11 , 22 , 33 , 44 },
{ 55 , 66 , 77 , 88 },
{ 99 , 110 , 121 , 132 }
};
int (* numPtr)[4 ] = numArr;
printf ("%p\n" , * numPtr);
printf ("%p\n" , * numArr);
printf ("%d\n" , numPtr[1 ][2 ]);
printf ("%p\n" , sizeof (numArr));
printf ("%p\n" , sizeof (numPtr));
return 0 ;
}
cs
위 코드와 같이 2차원 배열을 포인터에 할당할 수 있다.
2차원 배열을 포인터에 할당한 뒤 포인터를 역참조 해보면 세로 첫번째 요소의 주솟값이 나온다. 배열을 역참조해도 같은 값이나온다.
2차원 배열 포인터도 대괄호를 두 번 사용하여 배열의 요소에 접근할 수 있다.
sizeof로 크기를 계산하면 배열은 메모리가 차지하는 전체 공간이 출력되지만, 포인터는 포인터의 크기만 출력된다.
37.7 퀴즈
정답은 c이다.
정답은 d이다.
정답은 c이다.
정답은 b,d,e이다.
37.8 연습문제 : 행렬의 주대각선 성분 구하기
정답은 다음 코드와 같다.
for (int i = 0; i < sizeof(matrix) / sizeof(matrix[0]); i++)
{
printf("%d ", matrix[i][i]);
}
인덱스 값이 (0,0), (1,1) ... 처럼 가로 세로 모두 1씩 증가하기 때문에 0~7까지 반복한 값을 가로, 세로 인덱스에 넣으면 된다.
37.9 심사문제 : 전치 행렬 구하기
정답은 다음 코드와 같다.
int col = sizeof (matrix[0 ]) / sizeof (int );
int row = sizeof (matrix) / sizeof (matrix[0 ]);
for (int i = 0 ; i < row; i+ + )
{
for (int j = 0 ; j < col; j+ + )
{
printf ("%d " , matrix[j][i]);
}
printf ("\n" );
}
cs
행과 열이 서로 바뀌어있기 때문에 2차원 배열을 출력할 때 세로에 i를 넣고, 가로에 j를 넣으면 된다.
Unit 38. 포인터와 배열 응용하기
다음과 같이 프로그램 실행 중 원하는 크기 만큼 배열을 생성하는 기능은 gcc에서는 지원하지만 Viual Studio에서는 지원하지 않아 컴파일 에러가 발생한다.
#include < stdio.h>
int main()
{
int size ;
scanf ("%d" , & size );
int numArr[size ]; // 입력값을 배열의 크기로 사용
return 0 ;
}
cs
컴파일 에러 없이 배열의 크기를 동적으로 지정하려면 포인터를 선언하고 메모리를 할당한 뒤 메모리를 배열처럼 사용해야 한다.
38.1 포인터에 할당된 메모리를 배열처럼 사용하기
포인터에 malloc 함수로 메모리를 할당하여 포인터를 배열처럼 사용할 수 있다.
자료형 *포인터이름 = malloc(sizeof(자료형) * 크기);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include < stdio.h>
#include < stdlib.h>
int main()
{
int * numPtr = malloc (sizeof (int ) * 10 );
numPtr[0 ] = 10 ;
numPtr[9 ] = 20 ;
printf ("%d\n" , numPtr[0 ]);
printf ("%d\n" , numPtr[9 ]);
free (numPtr);
return 0 ;
}
cs
위 코드와 같이 int 크기에 10을 곱하여 동적으로 메모리를 할당하여 배열처럼 사용할 수 있다. 배열 처럼 대괄호 안데 인덱스를 지정하여 값을 저장하거나 가져올 수 있다.
배열은 한번 선언하면 끝이지만 위와 같은 포인터는 malloc함수로 메모리를 할당했기 때문에 free함수로 해제해야 한다.
*numPtr처럼 역참조 한것과 numPtr[0]으로 0번 인덱스를 가져오는 것은 같은 값을 가져오고, *(numPtr + 1)은 numPtr[1]과 같은 값을 가져오게 된다. 포인터에 값을 더하는 방식을 포인터 연산 이라고 한다.
38.2 입력한 크기만큼 메모리를 할당하여 배열처럼 사용하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include < stdio.h>
#include < stdlib.h>
int main()
{
int size ;
scanf ("%d" , & size );
int * numPtr = malloc (sizeof (int ) * size );
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 ;
}
cs
scanf로 크기를 입력받고, int 크기에 입력받은 크기를 곱하여 메모리를 할당했다.
입력받은 크기 만큼 반복하며 값을 할당 하고, 입력받은 크기만큼 반복하며 값을 출력하였다.
사용이 끝나면 free함수로 할당한 메모리를 해제해야 한다.
38.3 포인터에 할당된 메모리를 2차원 배열처럼 사용하기
2차원 배열처럼 사용하기 위해 포인터에 할당하는 방법은 다음과 같다.
1. 자료형 **포인터이름 = malloc(sizeof(자료형 *) * 세로크기); 와 같이 세로 공간 메모리 할당
2. 반복문으로 반복하며 포인터[i] = malloc(sizeof(자료형) * 가로크기); 와 같이 가로 공간 메모리 할당
3. 반복문으로 반복하면서 free(포인터[i]); 와 같이 가로 공간 메모리 해제
4. 반복문으로 반복하면서 free(포인터); 와 같이 세로 공간 메모리 해제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include < stdio.h>
#include < stdlib.h>
int main()
{
int * * m = malloc (sizeof (int * ) * 3 );
for (int i = 0 ; i < 3 ; i+ + )
{
m[i] = malloc (sizeof (int ) * 4 );
}
m[0 ][0 ] = 1 ;
m[2 ][0 ] = 5 ;
m[2 ][3 ] = 2 ;
printf ("%d\n" , m[0 ][0 ]);
printf ("%d\n" , m[2 ][0 ]);
printf ("%d\n" , m[2 ][3 ]);
for (int i = 0 ; i < 3 ; i+ + )
{
free (m[i]);
}
free (m);
return 0 ;
}
cs
2중 포인터에 2차원 배열의 세로 공간에 해당하는 메모리를 할당한다. 이때 세로 공간에는 값이 들어가지 않고 가로 공간의 메모리 주소가 들어간다. 그래서 sizeof(int *)처럼 포인터의 크기를 구한 후 세로 크기 3을 곱하는 것이다.
이후 세로 크기 만큼 반복하며 2차원 배열의 가로 공간에 해당하는 메모리를 할당한다.
가로 공간은 int 자료형의 크기에 가로크기 4를 곱해주었다.
이중 포인터를 2차원 배열처럼 사용하도록 메모리를 할당하는 모습을 그림으로 표현하면 다음과 같다.
m은 pointer to pointer to int이므로 int **m 처럼 선언한다.
2차원 배열을 사용하는 것처럼 [ ] [ ]에 세로인덱스, 가로인덱스를 지정하여 값을 할당하거나 가져올 수 있다.
포인터를 다 사용하면 가로 공간에 해당하는 메모리부터 해제한다. 이후 세로 공간에 해당하는 메모리를 해제한다.
메모리를 할당할 때는 세로 - > 가로의 순서로 할당했으므로 해제할 때는 가로 -> 세로의 순서로 해제하는 것이다.
38.4 입력한 크기만큼 메모리를 할당하여 포인터를 2차원 배열처럼 사용하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include < stdio.h>
#include < stdlib.h>
int main()
{
int row, col;
scanf ("%d %d" , & row, & col);
int * * m = malloc (sizeof (int * ) * row);
for (int i = 0 ; i < row; i+ + )
{
m[i] = malloc (sizeof (int ) * col);
}
for (int i = 0 ; i < row; i+ + )
{
for (int j = 0 ; j < col; j+ + )
{
m[i][j] = i + j;
}
}
for (int i = 0 ; i < row; i+ + )
{
for (int j = 0 ; j < col; j+ + )
{
printf ("%d " , m[i][j]);
}
printf ("\n" );
}
for (int i = 0 ; i < row; i+ + )
{
free (m[i]);
}
free (m);
return 0 ;
}
cs
세로 크기와 가로 크기를 입력 받고, 입력받은 값을 활용하여 세로공간과 가로공간 메모리를 할당하였다.
세로, 가로 크기가 고정되어 있지 않으므로 입력값 row, col을 활용하여 반복하면서 값을 할당하고, 값 할당이 끝나면 다시 반복하면서 2차원 배열의 값을 출력한다.
사용이 모두 끝나면 입력받은 세로 크기만큼 반복하며 가로 공간 메모리를 해제하고, 이후 세로 공간 메모리를 해제한다.
38.5 퀴즈
정답은 d이다.
정답은 b이다.
38.6 연습문제 : 포인터에 할당된 메모리를 3차원 배열처럼 사용하기
정답은 다음 코드와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1.
for (int depth = 0 ; depth < 2 ; depth+ + )
{
m[depth] = malloc (sizeof (long long * ) * 3 );
for (int row = 0 ; row < 3 ; row+ + )
{
m[depth][row] = malloc (sizeof (long long ) * 5 );
}
}
// 2.
for (int depth = 0 ; depth < 2 ; depth+ + )
{
for (int row = 0 ; row < 3 ; row+ + )
{
free (m[depth][row]);
}
free (m[depth]);
}
cs
3차원의 면에 해당하는 공간은 할당되어 있기 때문에 2중 for문으로 면의 개수만큼 반복하면서 세로공간에 해당하는 메모리를 할당하고, 안쪽 반복문에서 세로 크기만큼 반복하며 가로 공간에 해당하는 메모리를 할당하였다.
메모리를 해제할 때도 2차원 배열과 마찬가지로 할당의 역순으로 가로 -> 세로 -> 면 순서로 해제하면 된다.
38.7 심사문제 : 단위 행렬 만들기
정답은 다음 코드와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include < stdio.h>
#include < stdlib.h>
int main()
{
int size ;
scanf ("%d" , & size );
int * * m = malloc (sizeof (int * ) * size );
for (int i = 0 ; i < size ; i+ + )
{
m[i] = malloc (sizeof (int ) * size );
}
for (int i = 0 ; i < size ; i+ + )
{
for (int j = 0 ; j < size ; j+ + )
{
if (i = = j)
{
m[i][j] = 1 ;
}
else
{
m[i][j] = 0 ;
}
}
}
for (int i = 0 ; i < size ; i+ + )
{
for (int j = 0 ; j < size ; j+ + )
{
printf ("%d " , m[i][j]);
}
printf ("\n" );
}
for (int i = 0 ; i < size ; i+ + )
{
free (m[i]);
}
free (m);
return 0 ;
}
cs
가로 인덱스와 세로 인덱스가 같은 위치는 1이고, 다른 위치는 모두 0이므로 2차원 배열에 값을 할당하는 부분에서 if 조건문으로 해당 조건에 맞게 값을 저장하였다.
38.8 심사문제 : 지뢰찾기
정답은 다음 코드와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#define _CRT_SECURE_NO_WARNINGS
#include < stdio.h>
#include < stdlib.h>
int main()
{
int m, n;
int cnt = 0 ;
scanf ("%d %d" , & m, & n);
// 메모리 할당
char * * matrix = malloc (sizeof (char * ) * m);
for (int i = 0 ; i < m; i+ + )
{
matrix[i] = malloc (sizeof (char * ) * (n+ 1 ));
}
// 입력
for (int i = 0 ; i < m; i+ + )
{
scanf ("%s" , matrix[i]);
}
for (int i = 0 ; i < m; i+ + )
{
for (int j = 0 ; j < n; j+ + )
{
if (matrix[i][j] = = '.' )
{
matrix[i][j] = '0' ;
}
}
}
for (int i = 0 ; i < m; i+ + )
{
for (int j = 0 ; j < n; j+ + )
{
// 지뢰일 경우
if (matrix[i][j] = = '*' )
{
for (int y = i - 1 ; y < i + 2 ; y+ + )
{
for (int x = j - 1 ; x < j + 2 ; x+ + )
{
if (y < 0 | | x < 0 | | y > = m | | x > = n | | matrix[i][j] = = matrix[y][x] | | matrix[y][x] = = '*' )
{
continue ;
}
matrix[y][x] + = 1 ;
}
}
}
}
}
// 결과 출력
for (int i = 0 ; i < m; i+ + ) {
for (int j = 0 ; j < n; j+ + )
{
printf ("%c" , matrix[i][j]);
}
printf ("\n" );
}
// 메모리 해제
for (int i = 0 ; i < m; i+ + )
{
free (matrix[i]);
}
free (matrix);
return 0 ;
}
cs
행렬 크기와 행렬을 입력받고, 각 요소별로 반복하면서 '.' 인 경우 '0' 으로 바꿔 주었다.
각 요소별로 반복하면서 요소가 지뢰일 경우 그 요소를 둘러싸고 있는 요소들에 1씩 추가해 줬는데, 조건문을 사용하여 행렬을 벗어나지 않고, 둘러싸고 있는 요소가 지뢰가 아닐 경우에만 1씩 누적하였다.
반복문의 실행이 끝나면 다시 반복문으로 수정된 행렬의 결과를 출력하고 메모리를 해제하였다.