На какие операции и типы распадается имя динамического двухмерного массива?
Для примера, определим, чему эквивалентно выражение y[2][3] в нотации указателей и на какие шаги и типы оно распадается:
#include <iostream>
int main() {
int **y = new int *[5];
for (int i = 0; i != 5; ++i) {
y[i] = new int[7];
}
y[2][3] == *(*(y + 2) + 3);
// Шаг 1
static_assert(std::is_same_v<decltype(y), int **>);
// y — это указатель на массив указателей на массивы
// распада массива здесь не происходит, потому что
// y - это не массив, а указатель на указатель
// y - содержит адрес первого указателя: *y == y[0]
int **ptr_row_2 = y + 2; // получаем адрес указателя 2 (который содержит адрес строки 2)
// ptr_row_2 == &y[2]
// Шаг 2
int *&get_row_2 = *(y + 2); // первое обращение к памяти
// извлекаем указатель на первый элемент строки 2
// get_row_2 == y[2]
// Шаг 3
int *ptr_item_3 = *(y + 2) + 3; // получаем адрес на элемент 3 строки 2
// ptr_item_3 == &y[2][3]
// Шаг 4
int &get_item_3 = *(*(y + 2) + 3); // второе обращение к памяти
// извлекаем элемент 3 строки 2
// get_item_3 == y[2][3]
for (int i = 0; i != 5; ++i) {
delete[] y[i];
}
delete[] y;
return 0;
}
Вопрос: действительно ли первое обращение к памяти происходит на шаге 2? Или все таки на шаге 3?