В C++ на уровне языка не реализованы двумерные массивы, как это сделано, например, в C#. Здесь у нас есть лишь небольшой синтаксический сахар, в виде квадратных скобок.
Например, все эти выражения по обращению к третьему элементу массива
a равнозначны и будут компилироваться:
a[2] <=> *(a+2) <=> *(2+a) <=> 2[a]
О том, как это хранится в памяти
В случае объявления двумерного массива на стэке, грубо говоря, все строки записываются в одну длинную непрерывную область памяти-строку, а чтобы точно знать, какой они длинны (для адресации между строками) - нам нужно знать количество столбцов.
В случае динамического выделения памяти
const int ARRAY_SIZE = ...;
int **arr = new int*[ARRAY_SIZE];
for (int rown=0; rown<ARRAY_SIZE; ++rown) {
arr[rown] = new int[ARRAY_SIZE];
}
мы и вовсе будем иметь дело с массивом указателей на строки, которые разбросаны по памяти стандартным аллокатором.
Касаемо вопроса
Таким образом, только МЫ знаем, что эта штука - двумерный массив, а не просто указатель на некоторую область памяти, как об этом думает функция.
Пути решения в C++
Конечно, стоит присмотреться к специальным классам вроде
std::vector<T>
и
std::valarray<T>
, на основе которых можно строить что-то вроде
std::vector<std::vector<int>>
. Эти типы уже не голые указатели и несут в себе свойство size(), с помощью которого мы можем оценивать размерности.