Hateman31
@Hateman31
Делиться мыслями - это круто!

Почему нужно указывать количество столбцов?

Почему нельзя двумерный массив передать в функцию просто как указатель
на массив? Почему обязательно нужна вторая размерность?
Чтобы компилятор выделил память под конкретно массив?
Тогда почему можно игнорировать первую размерность, если
в аргументах можно написать так:
int test(int (*a)[5])
?
*a - это же обращение по адресу, по адресу a, нет?
Значит имя массива в данном контексте не тоже, что указатель??
  • Вопрос задан
  • 248 просмотров
Решения вопроса 2
@Free_ze
Пишу комментарии в комментарии, а не в ответы
В 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(), с помощью которого мы можем оценивать размерности.
Ответ написан
@Alexander1705
В памяти все элементы массива записываются по порядку, то есть последовательно. Двумерный массив можно рассматривать, как массив массивов, тогда станет понятно, что в памяти они записываются строка за строкой.

Предположим, у нас в памяти записан массив int arr[m][n];, но известен только адрес первого элемента - p = &arr[0][0];, и нам нужно получить доступ к элементу a[i][j] используя указатель p.
Сначала нам нужно сместится на i строк, то есть i*n элементов, а потом ещё на j элементов в текущей строке. Тогда искомый элемент - p[i*n + j].

Как видите, не зная длинны строки n мы не сможем получить доступ к элементу только по указателю и двум индексам, с другой стороны, количество строк m не влияет на результат, поэтому его можно опустить.

По аналогии всё происходит и с массивами большей размерности: нам нужно знать все величины кроме первой.

P. S. Можно добиться и другого поведения, только понадобится выделять память динамически, а после освобождать её.
void foo(int** arr)
{
    /* do something */
}

...

int** array = new int[m];
for (size_t i = 0; i < m; ++i)
{
    array[i] = new int[n];
}

foo(arr);

for (size_t i = 0; i < m; ++i)
{
    delete[] array[i];
}
delete[] array;
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы