Является ли этот код С++ переносимым? (при условии, что многомерные массивы имеют непрерывный макет памяти)
Прежде всего, извините за мой английский, если я делаю какие-либо грамматические ошибки и т.д.
Мой вопрос в том, что когда у нас есть двумерный массив, тогда, если я прав, с точки зрения компьютера и 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;
}
Ответы
Ответ 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
не является переносной операцией.