다차원 배열
다차원 배열이란 여러 개의 인덱스 값을 사용하도록 일차원 배열을 확장한 것이다.
예를 들어 틱택토 게임을 만들 때 3 x 3 격자를 이차원 배열로 구현할 수 있다.
다음 코드는 배열을 스택에 생성하고 0으로 초기화한 뒤 테스트 코드로 접근하는 예이다.
char board[3][3] = {};
// 텍스트 코드
board[0][0] = 'X'; // (0,0) 지점에 X를 둔다.
board[2][1] = 'O'; // (2,1) 지점에 O를 둔다.
여기서 첫 번째 인덱스가 x축을 가리키는지 아니면 y축을 가리키는지 헷갈릴 수 있다.
대부분 이해하기 쉽도록 첫 번째 인덱스를 x축으로, 두 번째 인덱스를 y축으로 사용한다.
다차원 스택 배열
스택에 생성한 이차원 배열의 메모리 상태는 다음과 같다.
실제로 메모리는 두 개의 축을 사용하지 않고 주소가 지정된 일차원 배열처럼 나열된다.
다차원 배열의 크기는 각 차원의 크기를 서로 곱한 값에 한 원소의 메모리 크기를 곱한 값과 같다.
위 코드에서 나온 3 x 3 보드를 표현한 배열의 크기는 3 x 3 x 1 = 9 바이트다.
여기서 char 자료형이 1바이트라 가정했다.
다차원 배열의 한 원소에 접근할 때 각 인덱스는 다차원 배열에 속한 하위 배열에 접근할 인덱스로 사용한다.
예를 들어 3 x 3 격자를 표현하는 배열에서 board[0]은 실제로 아래에서 회색으로 표시한 하위 배열을 가리킨다.
여기에 두 번째 인덱스(board[0][2])를 지정하면 앞서 가리킨 하위 배열에서 인덱스 2에 해당하는 원소를 가리킨다.
이와 같은 방식으로 N차원 배열을 처리한다.
다차원 힙 배열
다차원 배열에서 차원 수를 실행 시간에 결정하고 싶다면 힙 배열로 생성한다.
동적으로 할당하는 일차원 배열을 포인터로 접근하듯이 동적으로 할당하는 다차원 배열도 포인터로 접근한다.
이차원 배열의 경우 포인터에 대한 포인터로 원소에 접근한다.
다음과 같이 작성할 시 컴파일 에러가 발생한다.
char** board = new char[i][j]; // 버그, 컴파일 오류가 발생한다.
힙 배열에 대한 메모리 할당 방식은 스택 배열과 다르기 때문에 이렇게 작성하면 컴파일 에러가 발생한다.
힙에서는 메모리 공간이 연속적으로 할당되지 않기 때문에 스택 방식의 다차원 배열처럼 메모리를 할당하면 안 된다.
이럴 때는 힙 배열의 첫 번째 인덱스에 해당하는 차원의 배열을 연속적인 공간에 먼저 할당한다.
그런 다음 이 배열의 각 원소에 두 번째 인덱스에 해당하는 차원의 배열을 가리키는 포인터를 저장한다.
다음은 2 x 2 보드의 배열을 동적으로 할당할때의 모습이다.
여기서 하위 배열을 할당하는 작업은 컴파일러에서 자동으로 처리할 수 없다.
첫 번째 차원의 배열이 가리키는 각 원소(하위 배열)에 대한 메모리는 직접 하나씩 할당해야 한다.
다음 코드는 이차원 배열을 동적으로 할당하는 예이다.
char** allocateCharacterBoard(size_t xDimension, size_t yDimension)
{
char** myArray = new char*[xDimension]; // 첫 번째 차원의 배열을 할당한다.
for (size_t i = 0; i < xDimension; i++) {
myArray[i] = new char[yDimension]; // i번째 하위 배열을 할당한다.
}
return myArray;
}
다차원 힙 배열에 할당된 메모리를 해제할 때도 마찬가지로 delete[]로 하위 배열을 해제할 수 없기 때문에 일일이 해제해야 한다. 다차원 힙 배열을 해제하는 코드는 할당하는 코드와 연산을 반대로 할 뿐 형태는 비슷하다.
void releaseCharacterBoard(char** myArray, size_t xDimension)
{
for (size_t i = 0; i < xDimension; i++) {
delete[] myArray[i]; // i번째 하위 배열을 할당한다.
}
delete[] myArray; // 첫 번째 차원의 배열을 해제한다.
}
기존 C 스타일 배열은 메모리 안전성이 떨어지므로 가급적 사용하지 않는 것이 좋다.
C 스타일 배열도 소개한 이유는 기존에 작성된 코드를 봐야 할 수도 있기 때문이다.
코드를 새로 작성할 때는 std::array나 std::vector와 같은 C++ 표준 라이브러리에서 제공하는 컨테이너를 사용하자.
'💻프로그래밍 내용 정리 > C++17' 카테고리의 다른 글
[C++ 7.2.1] 배열과 포인터 (0) | 2022.08.24 |
---|---|
[C++ 7.1.4] 포인터 다루기 (1) | 2022.08.22 |
[C++ 7.1.3-1] 배열, 객체 배열, 배열 삭제 (1) | 2022.08.22 |
[C++ 7.1.2] 메모리 할당과 해제(new, delete / malloc(), free()), nothrow (0) | 2022.08.22 |
[C++ 7.1.1] 동적 메모리 작동 과정 살펴보기 (0) | 2022.08.22 |