다차원 배열
현실 세계의 데이터는 항상 일렬로만 존재하지 않습니다.
예를 들어 수학에서 행렬(Matrix)이나 표(Table), 게임의 맵(Map)과 같이 행과 열의 개념을 가지는 2차원 데이터, 또는 큐브(Cube)나 3D 공간처럼 3차원 이상의 데이터를 표현해야 할 때가 있습니다.
이러한 다차원적인 데이터를 저장하기 위해 C++에서는 다차원 배열(Multidimensional Array) 을 제공합니다.
이 장에서는 가장 흔하게 사용되는 2차원 배열을 중심으로 다차원 배열의 개념, 선언, 초기화, 그리고 요소 접근 방법에 대해 상세히 알아보겠습니다.
다차원 배열이란 무엇인가?
다차원 배열은 '배열의 배열'이라고 생각할 수 있습니다.
예를 들어 2차원 배열은 1차원 배열들의 배열이며, 3차원 배열은 2차원 배열들의 배열입니다.
C++에서는 다차원 배열을 선언할 때 각 차원의 크기를 대괄호 []
로 연달아 명시합니다.
주요 특징
- 행과 열: 2차원 배열은 흔히 '행(row)'과 '열(column)'로 구성된 표 형태의 데이터 구조로 시각화됩니다.
- 메모리 연속성: 1차원 배열과 마찬가지로, 다차원 배열의 모든 요소는 메모리에 연속적으로 저장됩니다. 2차원 배열의 경우, 일반적으로 행 단위로(row-major order) 메모리에 배치됩니다. 즉, 첫 번째 행의 모든 요소가 저장된 후 두 번째 행의 요소들이 저장되는 식입니다.
- 고정된 크기: 다차원 배열도 선언될 때 크기가 결정되며, 실행 중에 변경할 수 없습니다.
2차원 배열의 선언
2차원 배열을 선언할 때는 행의 크기와 열의 크기를 각각 대괄호 안에 명시합니다.
데이터타입 배열이름[행_크기][열_크기];
데이터타입
: 배열 요소들의 타입.배열이름
: 배열을 식별하는 이름.행_크기
: 배열의 행(row) 개수를 나타내는 양의 정수 상수.열_크기
: 배열의 열(column) 개수를 나타내는 양의 정수 상수.
int matrix[3][4]; // 3행 4열의 정수형 2차원 배열 선언 (총 3 * 4 = 12개 요소)
double scores[5][2]; // 5명의 학생의 2과목 점수를 저장할 2차원 배열
char board[8][8]; // 체스판이나 바둑판처럼 8x8 크기의 문자형 배열
2차원 배열의 초기화
2차원 배열도 1차원 배열과 유사하게 중괄호 {}
를 사용하여 초기화할 수 있습니다.
각 행을 별도의 중괄호로 묶어 명확하게 표현하는 것이 일반적입니다.
1. 모든 요소 초기화
// 3행 4열 배열 초기화
int matrix[3][4] = {
{1, 2, 3, 4}, // 0번 행 (matrix[0][0] ~ matrix[0][3])
{5, 6, 7, 8}, // 1번 행 (matrix[1][0] ~ matrix[1][3])
{9, 10, 11, 12} // 2번 행 (matrix[2][0] ~ matrix[2][3])
};
// 학생 3명의 국어, 영어 점수
int studentScores[3][2] = {
{90, 85}, // 0번 학생 (국어, 영어)
{70, 75}, // 1번 학생
{95, 90} // 2번 학생
};
2. 초기화 리스트의 행 크기 생략:
가장 첫 번째 대괄호 []
(행의 크기)는 생략할 수 있습니다. 이 경우 컴파일러가 초기화 리스트를 바탕으로 행의 개수를 자동으로 계산합니다. 하지만 열의 크기는 반드시 명시해야 합니다. 왜냐하면 열의 크기를 알아야 각 행의 시작 위치를 계산할 수 있기 때문입니다.
int numbers[][3] = { // 열의 크기는 3으로 명시
{1, 2, 3},
{4, 5, 6}
}; // numbers는 2행 3열 배열로 자동 결정 (총 6개 요소)
// 만약 열의 크기를 생략하면 컴파일 오류 발생: int invalid_array[][] = {{1,2},{3,4}}; (X)
3. 일부 요소만 초기화: 전체 요소를 초기화하지 않으면, 나머지 요소들은 1차원 배열과 마찬가지로 0 (zero) 으로 자동 초기화됩니다.
int partial[2][3] = {
{1, 2}, // 0번 행: {1, 2, 0}
{4} // 1번 행: {4, 0, 0}
};
// 최종적으로 { {1, 2, 0}, {4, 0, 0} }
2차원 배열 요소에 접근하기
2차원 배열의 각 요소는 두 개의 인덱스(행 인덱스, 열 인덱스)를 사용하여 접근합니다.
두 인덱스 모두 0부터 시작합니다.
배열이름[행_인덱스][열_인덱스];
행_인덱스
: 0부터행_크기 - 1
까지의 정수.열_인덱스
: 0부터열_크기 - 1
까지의 정수.
#include <iostream>
int main() {
int studentScores[3][2] = {
{90, 85}, // 0번 학생: 국어 90, 영어 85
{70, 75}, // 1번 학생: 국어 70, 영어 75
{95, 90} // 2번 학생: 국어 95, 영어 90
};
// 특정 요소에 접근
std::cout << "1번 학생의 국어 점수: " << studentScores[1][0] << std::endl; // 출력: 70
std::cout << "2번 학생의 영어 점수: " << studentScores[2][1] << std::endl; // 출력: 90
// 요소 값 변경
studentScores[0][1] = 88; // 0번 학생의 영어 점수를 85에서 88로 변경
std::cout << "변경된 0번 학생의 영어 점수: " << studentScores[0][1] << std::endl; // 출력: 88
// 중첩 루프를 사용하여 모든 요소 출력
// 행의 개수: sizeof(studentScores) / sizeof(studentScores[0])
// 열의 개수: sizeof(studentScores[0]) / sizeof(studentScores[0][0])
int numRows = sizeof(studentScores) / sizeof(studentScores[0]); // 3
int numCols = sizeof(studentScores[0]) / sizeof(studentScores[0][0]); // 2
std::cout << "\n모든 학생의 점수 표:" << std::endl;
for (int i = 0; i < numRows; ++i) { // 행(학생) 루프
std::cout << i << "번 학생: ";
for (int j = 0; j < numCols; ++j) { // 열(과목) 루프
std::cout << studentScores[i][j] << " ";
}
std::cout << std::endl;
}
/* 출력:
0번 학생: 90 88
1번 학생: 70 75
2번 학생: 95 90
*/
return 0;
}
3차원 이상의 다차원 배열
C++은 3차원 이상의 배열도 지원합니다. 3차원 배열은 배열이름[깊이][행][열]
과 같은 형식으로 생각할 수 있습니다.
각 차원의 인덱스를 추가하여 접근합니다.
데이터타입 배열이름[차원1_크기][차원2_크기][차원3_크기]...;
int cube[2][3][4]; // 2개의 3행 4열 평면을 가진 3차원 배열
// 초기화 예시
int cube_data[2][2][2] = {
{{1, 2}, {3, 4}}, // 첫 번째 "슬라이스" (0번 깊이)
{{5, 6}, {7, 8}} // 두 번째 "슬라이스" (1번 깊이)
};
// 요소 접근 예시
std::cout << cube_data[0][1][0] << std::endl; // 출력: 3 (첫 번째 슬라이스, 두 번째 행, 첫 번째 열)
std::cout << cube_data[1][0][1] << std::endl; // 출력: 6 (두 번째 슬라이스, 첫 번째 행, 두 번째 열)
// 모든 요소 출력 (중첩 for 루프 3개)
for (int d = 0; d < 2; ++d) { // 깊이
for (int r = 0; r < 2; ++r) { // 행
for (int c = 0; c < 2; ++c) { // 열
std::cout << cube_data[d][r][c] << " ";
}
std::cout << std::endl;
}
std::cout << std::endl; // 각 슬라이스 구분
}
다차원 배열의 차원이 많아질수록 코드의 복잡성이 증가하고, 가독성이 떨어질 수 있습니다.
일반적으로 2차원 배열이 가장 흔하게 사용되며, 3차원 이상은 특수한 경우에만 고려하는 것이 좋습니다.
다차원 배열과 인덱스 범위 초과
1차원 배열과 마찬가지로, 다차원 배열에서도 인덱스 범위를 초과하여 접근하는 것은 심각한 런타임 오류(예: 메모리 액세스 위반)를 유발할 수 있습니다.
C++ 컴파일러는 이러한 오류를 잡아주지 않으므로, 프로그래머가 각 차원의 유효한 인덱스 범위를 철저히 지켜야 합니다.
for
루프를 사용하여 다차원 배열을 순회할 때는 각 루프의 조건이 해당 차원의 크기를 정확히 반영하도록 주의해야 합니다.