Is “int * ptr = * ((& a) + 1);” где “a” является int , четко определенным Стандартом?

Основываясь на этом вопросе ( странная проблема с выпуском в c ), был получен ответ ( предоставленный @Lundin ) об этой строке:

int *ptr = (int*)(&a+1); 

где он сказал:

the cast (int*) was hiding this bug .

Поэтому я пришел со следующим:

 #include  int main( void ){ int a[5] = {1,2,3,4,5}; int *ptr = *( ( &a ) + 1 ); printf("%d", *(ptr-1) ); } 

Я хотел бы знать, если это:

 int *ptr = *( ( &a ) + 1 ); 

Является ли это стандартом?

EDIT :

В какой-то момент @chux указал на п. §6.3.2.3.7 который:

 A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object. 

Но я не уверен, правильно ли я это понимаю.

Это выражение вызывает неопределенное поведение в результате оператора разыменования * :

 int *ptr = *( ( &a ) + 1 ); 

Сначала начнем с ( &a ) + 1 . Эта часть действительна. &a имеет тип int (*)[5] , то есть указатель на массив размера 5. Выполнение арифметики указателя путем добавления 1 является допустимым, хотя a не является элементом массива.

В разделе 6.5.6 стандартного подробного описания аддитивных операторов в пункте 7 говорится:

Для целей этих операторов указатель на объект, который не является элементом массива, ведет себя так же, как указатель на первый элемент массива длиной один с типом объекта в качестве его типа элемента.

Также разрешено создавать указатель, указывающий на один элемент за концом массива. Так что &a + 1 разрешено.

Проблема заключается в том, когда мы разыгрываем это выражение. В пункте 8 говорится:

Когда выражение, которое имеет целочисленный тип, добавляется или вычитается из указателя, результат имеет тип операнда указателя. Если операнд указателя указывает на элемент объекта массива, и массив достаточно велик, результат указывает на смещение элемента от исходного элемента, так что разность индексов результирующих и исходных элементов массива равна целочисленному выражению. Другими словами, если выражение P указывает на i-й элемент объекта массива, выражения (P) + N (эквивалентно, N + (P) ) и (P) -N (где N имеет значение n) соответственно, i + n-й и i-й-элементы элемента массива, если они существуют. Более того, если выражение P указывает на последний элемент объекта массива, выражение (P) +1 указывает один за последним элементом объекта массива, а если выражение q указывает один за последним элементом объекта массива, выражение (Q) -1 указывает на последний элемент объекта массива. Если оба операнда указателя и результат указывают на элементы одного и того же объекта массива или один за последним элементом объекта массива, оценка не должна приводить к переполнению; в противном случае поведение не определено. Если результат указывает один за последним элементом объекта массива, он не должен использоваться как операнд унарного * оператора, который оценивается.

Поскольку разыменование указателя на один конец, конец массива не разрешен, beahvior не определен.

Возвращаясь к выражению в указанной статье:

 int *ptr = (int*)(&a+1); printf("%d %d", *(a+1), *(ptr-1)); 

Это также неопределенное поведение, но по другой причине. В этом случае int (*)[5] преобразуется в int * и впоследствии используется преобразованное значение. Единственным случаем, когда использование такого преобразованного значения является законным, является преобразование указателя объекта в указатель на тип символа, например char * или unsigned char * и затем разыменовывается, чтобы читать байты представления объекта.

РЕДАКТИРОВАТЬ:

Кажется, что две строчки выше действительно хорошо определены. Во время разворота указателя *(ptr-1) происходит обращение к объекту, имеющему эффективный тип int , который соответствует разыменованному типу ptr-1 . Вычисление значения указателя &a+1 из int (*)[5] в int * является допустимым, а выполнение арифметики указателя на значение casted pointer также действительным, поскольку оно указывает либо внутри a либо одного элемента за ним.

*( ( &a ) + 1 ) является UB из-за

… Если результат указывает один за последний элемент объекта массива, он не должен использоваться как операнд унарного * оператора, который оценивается. C11 §6.5.6 8

( &a ) + 1 указывает на «одно прошлое». Использование * по этому идет против «не должно».

 int a[5] = {1,2,3,4,5}; int *ptr = *( ( &a ) + 1 ); 

Даже если a было int a это относится к

Для целей этих операторов указатель на объект, который не является элементом массива, ведет себя так же, как указатель на первый элемент массива длиной один с типом объекта в качестве его типа элемента. §6.5.6 7

int *ptr = *( ( &a ) + 1 ); вызывается неопределенное поведение.

C11 – §6.5.6 «Аддитивные операторы» (P8):

Когда выражение, которое имеет целочисленный тип, добавляется или вычитается из указателя, результат имеет тип операнда указателя. Если операнд указателя указывает на элемент объекта массива, и массив достаточно велик, результат указывает на смещение элемента от исходного элемента, так что разность индексов результирующих и исходных элементов массива равна целочисленному выражению. Другими словами, если выражение P указывает на i элемент объекта массива, выражения (P)+N (эквивалентно, N+(P) ) и (P)-N (где N имеет значение n ) соответственно, i+n -th и i−n ые элементы массива, если они существуют. Более того, если выражение P указывает на последний элемент объекта массива, выражение (P)+1 указывает один за последним элементом объекта массива, и если выражение Q указывает один за последним элементом объекта массива, выражение (Q)-1 указывает на последний элемент объекта массива. […]