В c++ есть запись математически схожая с доступом к элементам матрице в математике запись:
m[x][y] - т.е. мы берем элемент xy матрицы m (формально наоборот конечно, сначала строки Y потом колонки X но в конечном счете разницы никакой, кроме как если важна оптимизация).
В реальности же эта запись означает что мы берем x-ый массив в списке m и у полученного массива берем y-ый элемент, т.е. m имеет тип
массив массивов элементов, и для работы с ним его нужно инициализировать, создав нужное количество массивов одинаковой размерности.
p.s. иногда, когда хочется 'простоты' инициализации, уменьшения фрагментации памяти, более эффективный доступ последовательный к элементам, можно хранить массив в виде одномерного массива всех элементов матрицы, а доступ к элементам получать, вычисляя смещение в этом массиве как x+y*N, где N - размер матрицы по X, т.е. m[x+y*N], зато когда надо работать последовательно со всеми элементами с лева направо, сверху вниз, можно просто работать по смещению в этом одномерном массиве m[i++]
Теперь дальше - создаем массив, размерностью M (количество строк матрицы), указателей на массивы элементов, и инициализируем его указателями на каждый N-ый элемент в этом одномерном массиве элементов, и получаем возможность работать с одномерным массивом как с двумерным синтаксически, сохраняя возможность быстрого поэлементного доступа...
const int N=3;
const int M=2;
int ml[N*M]={1,1,1,2,2,2};
// матрица:
// 111
// 222
int* m[M];
for(int i=0;i<M;i++) m[i]=*(ml[i*N]);
//
for(int i=0;i<M*N;i++) ml[i]...
и
for(int y=0;i<M;i++)
for(int x=0;i<N;i++)
m[y][x]... // порядок колонки строки тут перевернут, но можно заранее это учитывать и перевернуть везде в коде для удобства восприятия
p.p.s. может где то ошибся но не принципиально, сто лет не писал на си