Подтвердить что ты не робот

Является ли этот код С++ переносимым? (при условии, что многомерные массивы имеют непрерывный макет памяти)

Прежде всего, извините за мой английский, если я делаю какие-либо грамматические ошибки и т.д.

Мой вопрос в том, что когда у нас есть двумерный массив, тогда, если я прав, с точки зрения компьютера и C/С++, это просто длинный одномерный массив, индексы просто помогают компилятору сопоставлять к конкретному адресу.

Этот фрагмент кода работает в Visual С++, однако я хотел бы знать, соответствует ли этот код переносимым и стандарту ( С++ 98), не вызывая сюрпризов для других архитектур и/или операционных систем:

int arr[][3] = { 1, 5, 3, 7, 5, 2, 7, 8, 9 };
const int ARR_NUM = sizeof(arr) / sizeof(int);

int* ptr = reinterpret_cast<int*>(arr);    // NOT: int(*)[][3] !!!
for (int i = 0; i < ARR_NUM; ++i) {
    cout << ptr[i] << endl;
}
4b9b3361

Ответ 1

Standardese

Элементы многомерного массива сохраняются последовательно в порядке строк, поэтому ручная индексация переносима:

С++ 98, 8.3.4/1:

Объект типа массива содержит смежно выделенную непустую набор из N под-объектов типа T.

Очевидно, что для многомерного массива это рекурсивно применяется.

Однако это использование reinterpret_cast не переносимо. В стандарте говорится (С++ 98, 5.2.10/1), что

[...] В противном случае результатом будет r-значение и [...], array-to-pointer, [...] стандартные преобразования выполняются на выражение v.

Другими словами, передача arr сразу же запускает распад массива на указатель на его первый элемент. Затем (С++ 98, 5.2.10/3) появляется catch-all

Отображение, выполненное с помощью reinterpret_cast, определяется реализацией.

В оставшейся части раздела перечислены некоторые исключения, указывающие отличные отбрасываемые команды. Поскольку ни один из них не применяется здесь, вывод заключается в том, что технически это реализация, определяемая по умолчанию.

Окончательное заключение

Теоретически это не переносимо. Практически, если архитектура одинакова (например, x86), я бы наверняка ожидал, что приведение будет надежно работать.

К счастью, вам не нужно предполагать ничего подобного, потому что другие упомянули, что-то вроде int* ptr = arr[0] делает то же самое и гарантированно переносится.

Ответ 2

Если вы хотите быть действительно строгим, reinterpret_cast не очень хорошо определен в стандарте. Это будет работать повсюду, но вы можете сделать педантичный случай против него.

Использование

int *ptr = arr[0];

чтобы быть на самой безопасной стороне. Массив смежного массива гарантирован.

Ответ 3

Что касается предположения, что многомерные массивы имеют непрерывный макет памяти, он соответствует стандартам и переносимым. Это правда со времен C, а С++ не меняет этого.

Однако reinterpret_cast не переносится, поэтому в целом ваш код не гарантирует работу везде.

Ответ 4

Если я не ошибаюсь, reinterpret_cast не является переносной операцией.