Это выражение вызывает неопределенное поведение в результате оператора разыменования *
:
int *ptr = *( ( &a ) + 1 );
Сначала давайте начнем с ( &a ) + 1
. Эта часть действительна. &a
имеет тип int (*)[5]
, то есть указатель на массив размером 5. Выполнение арифметики с указателем путем добавления 1 допустимо, даже если a
не является элементом массива.
В разделе 6.5.6 стандарта C , подробно описывающем Аддитивные операторы, параграф 7 гласит:
Для целей этих операторов указатель на объект, который
не элемент массива ведет себя так же, как указатель на первый
элемент массива длиной один с типом объекта в качестве его
тип элемента.
Также разрешено создавать указатель, который указывает на один элемент после конца массива. Так что &a + 1
разрешено.
Проблема в том, что мы разыменовываем это выражение. Пункт 8 гласит:
Когда выражение с целочисленным типом добавляется или вычитается
из указателя результат имеет тип операнда указателя. Если
операнд указателя указывает на элемент объекта массива и массив
достаточно велик, результат указывает на смещение элемента от
исходный элемент такой, что разница индексов
результирующие и исходные элементы массива равны целочисленному выражению.
Другими словами, если выражение P указывает на i-й элемент
объект массива, выражения (P) + N (эквивалентно, N + (P) ) и (P) -N
(где N имеет значение n) указывают соответственно на i + n-й и
i -ные элементы массива, если они существуют. Более того, если
выражение P указывает на последний элемент объекта массива,
выражение (P) + 1 указывает один за последним элементом массива объекта,
и если выражение q указывает на один последний элемент массива
объект, выражение (Q) -1 указывает на последний элемент массива
объект. Если и операнд указателя, и результат указывают на элементы
одного и того же объекта массива, или один за последним элементом массива
объект, оценка не должна производить переполнение; в противном случае
поведение не определено. Если результат указывает на один последний элемент
объекта массива, он не должен использоваться в качестве операнда унарного *
оцениваемый оператор.
Поскольку разыменование указателя на один конец конца массива не допускается, поведение не определено.
Возвращаясь к выражению в ссылочном посте:
int *ptr = (int*)(&a+1);
printf("%d %d", *(a+1), *(ptr-1));
Это также неопределенное поведение, но по другой причине. В этом случае int (*)[5]
преобразуется в int *
, и впоследствии используется преобразованное значение. Единственный случай, когда использование такого преобразованного значения является допустимым, - это преобразование указателя объекта в указатель на тип символа, например char *
или unsigned char *
и впоследствии разыменовывается для чтения байтов представления объекта.
EDIT:
Кажется, две строки выше на самом деле хорошо определены. В то время, когда происходит разыменование указателя *(ptr-1)
, объект, к которому осуществляется доступ, имеет эффективный тип int
, который соответствует разыменованному типу ptr-1
. Преобразование значения указателя &a+1
из int (*)[5]
в int *
допустимо, и выполнение арифметики указателя для приведенного значения указателя также допустимо, поскольку оно указывает либо внутри a
, либо на один элемент после него.