C++에서 동적 2차원 배열은 본질적으로 '배열을 가리키는 포인터들의 배열'입니다. 먼저 포인터 배열을 할당한 뒤, 각 포인터가 하나의 행(row)에 해당하는 1차원 배열을 가리키는 구조로 만들어집니다.
다음은 크기가 3 × 4인 2차원 배열의 구조를 나타낸 다이어그램입니다.

알고리즘
시작 배열의 차원(크기)을 선언한다. new를 사용해 2차원 배열 a[][]를 동적으로 할당한다. 배열에 값을 채운다. 배열을 출력한다. delete로 메모리를 해제한다. 종료
예제 코드
#include <iostream>
using namespace std;
int main() {
int B = 4; // 행(row)의 개수
int A = 5; // 열(column)의 개수
// 1단계: 포인터 배열(B개)을 먼저 할당
int** a = new int*[B];
// 2단계: 각 행마다 길이가 A인 int 배열을 할당
for(int i = 0; i < B; ++i)
a[i] = new int[A];
// 3단계: 배열에 값 채우기
for(int i = 0; i < B; ++i)
for(int j = 0; j < A; ++j)
a[i][j] = i;
// 4단계: 배열 출력
for(int i = 0; i < B; ++i)
for(int j = 0; j < A; ++j)
cout << a[i][j] << "\n";
// 5단계: 할당된 메모리 해제
for(int i = 0; i < B; ++i)
delete [] a[i];
delete [] a;
return 0;
}
출력 결과
0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
코드 단계별 설명
1단계 – 포인터 배열 생성: int** a = new int*[B];는 int형 포인터 B개를 담는 배열을 힙(heap)에 할당합니다. 이 포인터들이 각각 하나의 행을 가리키게 됩니다.
2단계 – 각 행 할당: 반복문으로 각 포인터 a[i]에 길이가 A인 int 배열을 새로 할당합니다. 이 방식의 장점은 필요하다면 각 행의 길이를 서로 다르게 설정할 수도 있다는 점입니다(톱니형 배열).
3~4단계 – 초기화와 출력: 이중 반복문으로 a[i][j] = i;와 같이 값을 저장한 뒤 순서대로 출력합니다. 그 결과 i번째 행에는 모두 i라는 값이 채워져 화면에 나타납니다.
5단계 – 메모리 해제: new로 할당한 메모리는 반드시 delete로 해제해야 메모리 누수를 방지할 수 있습니다. 먼저 각 행을 delete[] a[i]로 해제한 후, 마지막에 포인터 배열 자체를 delete[] a로 해제하는 순서를 지켜야 합니다.
추가 팁
이 방식은 프로그램 실행 중에 배열의 크기를 결정할 수 있다는 점에서 정적 2차원 배열과 가장 큰 차이가 있습니다. 한편 C++11 이상에서는 std::vector<std::vector<int>>나 스마트 포인터를 활용하면 메모리 해제를 자동으로 처리할 수 있어 더욱 안전한 대안이 됩니다.