Ответ 1
Мы должны начать с проверки того, что такое int [5] [5]. Используемые типы:
- ИНТ
- массив [5] из ints
- массив [5] массивов
Не существует массива [25] из ints.
Правильно, что из семантики sizeof следует, что массив в целом смежный. Массив [5] из int должен иметь 5 * sizeof (int) и рекурсивно применяться, а [5] [5] должен иметь размер 5 * 5 * sizeof (int). Нет дополнительного места для заполнения.
Кроме того, массив в целом должен работать, когда заданы memset, memmove или memcpy с sizeof. Также должно быть возможно выполнить итерацию по всему массиву с помощью (char *). Итак, действительная итерация:
int a[5][5], i, *pi;
char *pc;
pc = (char *)(&a[0][0]);
for (i = 0; i < 25; i++)
{
pi = (int *)pc;
DoSomething(pi);
pc += sizeof(int);
}
Выполнение такого же действия с (int *) будет иметь поведение undefined, потому что, как сказано, нет массива [25] из int. Использование союза, как и в случае Кристофа, тоже должно быть верным. Но есть еще один момент, усложняющий это далее: оператор равенства:
6.5.9.6 Два указателя сравнивают одинаковые, если и только если оба являются нулевыми указателями, оба являются указателями на один и тот же объект (включая указатель на объект и подобъект в начале) или функцию, оба являются указателями на один последний элемент одного и того же массива object, или один - указатель на один конец конца одного объекта массива, а другой - указатель на начало другого объекта массива, который происходит сразу же после первого объекта массива в адресном пространстве. 91)
91). Два объекта могут быть смежными в памяти, поскольку они являются смежными элементами более крупного массива или соседних элементов структуры без прокладки между ними или потому, что реализация выбрала их так, даже если они не связаны друг с другом. Если ранее недействительные операции с указателем (например, доступ к границам внешнего массива) вызвали поведение undefined, последующие сравнения также приводят к поведению undefined.
Это означает:
int a[5][5], *i1, *i2;
i1 = &a[0][0] + 5;
i2 = &a[1][0];
i1 сравнивается как равное i2. Но при итерации по массиву с помощью (int *) поведение по-прежнему undefined, потому что оно изначально получено из первого подмассива. Он не волшебным образом преобразует указатель во второй подмассив.
Даже при выполнении этого
char *c = (char *)(&a[0][0]) + 5*sizeof(int);
int *i3 = (int *)c;
не поможет. Он сравнивается с i1 и i2, но не выводится ни из одного из подмассивов; это указатель на один int или массив [1] из int в лучшем случае.
Я не считаю это ошибкой в стандарте. Другое дело: разрешить это ввести специальный случай, который нарушает либо систему типов для массивов, либо правила для арифметики указателей или и то, и другое. Это может считаться отсутствующим определением, но не ошибкой.
Итак, даже если макет памяти для [5] [5] идентичен макету [25], и тот же цикл, в котором используется (char *), может использоваться для итерации по обоим, реализация может взорваться, если используется как другая. Я не знаю, почему он должен знать или знать какую-либо реализацию, которая могла бы быть, и, возможно, в стандарте не упоминается ни одного факта, который до сих пор не упоминается, что делает его четко определенным поведением. До тех пор я бы считал, что это undefined и оставаться на безопасной стороне.