Добавлю ко всему выше написанному: подобное использование двумерных массивов убивает производительность, т.к. вместо одного чтения памяти происходит 2, а кроме того весь массив разбит на много маленьких кусков, которые лежат в разных местах памяти - кэш процессора используется не эффективно. В нагруженных приложениях это будет сказываться.
Правильнее использовать двумерный массив выделенный одним куском и пересчитывать индексы в ручную:
int *a = malloc(sizeof(int) * n * m);
for(int i=0; i < n; ++i)
for(int j=0; j < m; ++j)
*(a + i * m + j) = 0;
Это стандартный вариант использования двумерного массива, который будет работать везде и на С++ то же.
Что бы избежать пересчета индексов (например когда массивы трехмерные или больше) можно использовать промежуточный "указатель на массив переменной длины" (правда это будет работать только на gcc/clang и в С++ работать не будет, только С99+). Подробно расписывать не буду, т.к. тут важно понимание. Если будет интересно в интернете информацию по VLA найдете.