Ответ 1
Цель, по крайней мере, с тех пор, как стандартизация C в 1989 году заключалась в том, что реализациям разрешено проверять границы массива для доступа к массиву.
Элемент p->a
является объектом типа int
. C11 6.5.6p7 говорит, что
7 Для целей [аддитивных операторов] указатель на объект, который не является элементом массива, ведет себя так же, как указатель на первый элемент массива длиной один с типом объекта как его тип элемента.
Таким образом,
&(p->a)
является указателем на int
; но он также выглядит как указатель на первый элемент массива длины 1, а int
- тип объекта.
Теперь 6.5.6p8 позволяет вычислить &(p->a) + 1
, который является указателем только на конец массива, поэтому нет undefined. Однако разыменование такого указателя недействительно. Из Приложение J.2, где указано, поведение undefined, когда:
Добавление или вычитание указателя на объект массива или целочисленный тип или только за его пределами приводит к результату, который указывает непосредственно за объект массива и используется как операнд унарного оператора
*
, который оценивается (6.5 0,6).
В приведенном выше выражении существует только один массив, один (как будто) с ровно 1 элементом. Если &(p->a) + 1
разыменовывается, массив с длиной 1 получает доступ за пределы и undefined поведение происходит, то есть
поведение [...], для которого [The C11] Standard не предъявляет требований
Возможные диапазоны поведения undefined: от игнорируются полностью с непредсказуемыми результатами, ведут себя во время перевода или выполнения программы документированным образом, характерным для среды (с выдачей диагностического сообщения или без него), до завершения перевода или выполнения (с выдачей диагностического сообщения).
То, что наиболее распространенное поведение полностью игнорирует ситуацию, т.е. ведет себя так, как если бы указатель ссылался на ячейку памяти сразу после этого, не означает, что другой тип поведения не будет приемлемым со стандартной точки зрения - стандартный позволяет любой мыслимый и невообразимый результат.
Утверждалось, что стандартный текст С11 написан смутно, и намерение комитета должно состоять в том, чтобы это действительно было разрешено, и раньше было бы все в порядке. Это не верно. Прочтите часть ответа комитета на [Отчет о дефектах № 017 от 10 декабря 1992 года до C89].
Вопрос 16
[...]
ответ
Для массива массивов разрешенная арифметика указателя в подпункт 6.3.6, стр. 47, строки 12-40 следует понимать интерпретация использования объекта слова как обозначение объект, определяемый непосредственно типом и значением указателя, а не другим объекты, связанные с этим, соприкосновением. Поэтому, если выражение превышает эти разрешения, поведение undefined. Например, следующий код имеет поведение undefined:
int a[4][5]; a[1][7] = 0; /* undefined */
Некоторые соответствующие реализации могут выберите диагностику нарушения границ массива, в то время как другие могут выбрать, чтобы успешно использовать такие попытки доступа с помощью очевидная расширенная семантика.
(выделено жирным шрифтом)
Нет причин, по которым то же самое не будет передано скалярным членам структур, особенно когда 6.5.6p7 говорит, что указатель на них следует считать таким же, как указатель на первый элемент массива длиной один с типом объекта в качестве его типа элемента.
Если вы хотите обратиться к последовательным struct
s, вы всегда можете перенести указатель на первый член и направить его как указатель на struct
, а затем:
*(int *)((S *)&(p->a) + 1) = 0;