Является ли поведение ++ * ptr ++ undefined в с ++?

В тесте мне задали следующий вопрос (я не хотел писать его сам. Его задавал тест. Я все еще знаю его плохой код) об оценке ++ * ptr ++

int Ar[ ] = { 6 , 3 , 8 , 10 , 4 , 6 , 7} ;
int *Ptr = Ar  ;
cout<<++*Ptr++  ;

Однако я подозреваю, что это неопределенное поведение, поскольку это может быть как (++*ptr)++, так и ++(*ptr++). Это? Я плохо знаком с документацией, поэтому ничего не нашел.

Что сделал с вами C++, что вы чувствуете себя обязанным это сделать?

tadman 20.12.2018 19:40

Нет, это правильно.

Igor R. 20.12.2018 19:41

Когда вы спрашиваете о «неопределенном поведении», лучше всего сначала заглянуть в документацию.

tadman 20.12.2018 19:41

Зачем вообще писать такой код, независимо от его правильности?

Matteo Italia 20.12.2018 19:41

@soham: Вообще говоря, вопрос о сложных взаимодействиях нескольких экземпляров ++ и тому подобном, который представляет собой код, который должен никогда не быть написанным, вызовет отрицательные голоса. Или, другими словами, если вам нужно спросить, четко ли он определен, не пишите так.

Nicol Bolas 20.12.2018 19:41

@tadman извини, но я не понял твою точку зрения

Harmonic 20.12.2018 19:42

@soham Как ты думаешь, почему это не определено? Я думаю, вы получили бы меньше голосов против, если бы вы объяснили это в вопросе.

HolyBlackCat 20.12.2018 19:42

Комбинирование операторов префикса и постфиксного приращения жестоко по отношению к любому, кто пытается прочитать этот код и понять ваши намерения. Если вы подозреваете, что это неопределенное поведение, первым делом следует изучить спецификацию C++ или хороший справочник по операторам пре- и пост-инкремента и тому, как они могут работать с операцией разыменования указателя.

tadman 20.12.2018 19:42

@MatteoItalia Это было задано в тесте, сэр ... Я не хотел писать это сам

Harmonic 20.12.2018 19:43

Если вы действительно хотите это сделать, возможно, попробуйте переписать его как ++(*(ptr++)). Легче понять.

Pavan Yalamanchili 20.12.2018 19:43

@soham Обычно такие уродливые выражения вызывают UB, если / когда они изменяют переменную более одного раза в одном месте. Поскольку этот нет (один ++ изменяет указатель, другой ++ изменяет указанный int), он четко определен.

HolyBlackCat 20.12.2018 19:45

@tadman на самом деле, если вы подозреваете, что он не определен, первый и единственный шаг - переписать выражение, устраняющее сомнения.

Slava 20.12.2018 19:47

@soham правильный ответ на этот тест "человек, написавший это, должен быть уволен"

Slava 20.12.2018 19:48

Вы должны были отметить вопрос как языковый юрист. Тогда комментарии типа «почему вы это сделали?» Были бы неприменимы, и вопрос был бы рассмотрен по существу.

SergeyA 20.12.2018 19:50

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

user4581301 20.12.2018 19:50

Это просто; он же cout<<(*Ptr+++=1); ;-)

molbdnilo 20.12.2018 20:22

Я думаю, что это на самом деле хороший контрольный вопрос, потому что он проверяет, понимаете ли вы, что ++*ptr делает что-то отличное от *++ptr, и формулировка этого таким образом заставляет тестируемого задействовать свой мозг, а не повторять заученный факт.

zwol 20.12.2018 21:24

В этом коде четко определено поведение разработчика, которого сильно бьет по голове тот, кто должен это поддерживать.

IMil 21.12.2018 01:18

@zwol: OTOH, это плохой вопрос для тех, кто (как я) использует скобки для принудительного выполнения порядка оценки, а не зависит от моей памяти о правилах приоритета. Знаете, у меня так много мозговых клеток, и мне есть чем заняться с ними :-)

jamesqf 21.12.2018 04:01

также может быть записан как ++Ptr++[0]

M.M 21.12.2018 08:19

Вот почему каждый программист на C / C++ должен иметь свою таблицу приоритетов операторов под своей клавиатурой (en.wikipedia.org/wiki/…) ...

cmaster - reinstate monica 21.12.2018 12:41

Я бы предпочел ++(++Ptr)[-1] или даже ++-1[++Ptr], потому что пре-инкремент всегда лучше пост-инкремента (этот комментарий может содержать сарказм).

chtz 21.12.2018 13:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
56
22
3 429
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

However I suspect this is undefined behaviour since it can be both (++*ptr)++ or ++(*ptr++). Is it?

Не совсем, в отличие от поведения во время выполнения, которое дает широкую свободу действий разработчикам, в C++ сам синтаксический анализ следует довольно строгим и четко определенным правилам 1. Действительно, если посмотреть на правила приоритета, ++*Ptr++ фактически анализируется как ++(*(Ptr++)).

Вместо этого этот вопрос с подвохом, вероятно, намекает на неопределенное поведение выражений, таких как i = ++i + ++i, где у вас есть значение, которое появляется несколько раз в выражении и подвергается модификации побочным эффектом самого выражения. Такие выражения являются недопустимыми, поскольку, если не существует какого-либо оператора, который устанавливает последовательность побочных эффектов 2, точный момент их применения не определен, поэтому не определено, какие именно значения i примет в различных точках выражения.

Тем не менее, здесь нет неопределенного поведения, поскольку все побочные эффекты в выражении работают с разными значениями, которые появляются в выражении только один раз: «внутренний» ++ влияет на Ptr, а внешний влияет на значение, указанное изначально Ptr, то есть Ar[0]. .

++(*(Ptr++))
     ^^^^^____increments Ptr, returning its original value
   ^^^^^^^^______dereferences the original Ptr, AKA &Ar[0]
^^^^^^^^^^^^_______ increments Ar[0]

При этом, если бы я когда-либо видел такое выражение в нашей кодовой базе, я бы пошел на все, чтобы найти автора и убедиться, что это больше не повторится.


  1. Иногда бывает очень странно и нелепо дорого реализовать. Тем не менее, в стандарте есть экземпляры неопределенного поведения являются, описывающие некоторые угловые случаи синтаксического анализа, но они на порядки менее распространены, чем неопределенное поведение во время выполнения.
  2. Удобное резюме этих правил можно найти здесь; Интересно, что в C++ 17 были добавлены некоторые дополнительные гарантии.

@val что сделало это законным?

Krupip 20.12.2018 21:28

Поведение i = ++i + ++i не определено в C++ 17.

Jans 20.12.2018 21:32

@val: только что проверил еще раз: это незаконно. C++ 17 просто добавил точку последовательности для операторов присваивания и составного присваивания. stackoverflow.com/a/46171943/214671++i + ++i остается незаконным даже сам по себе (без присваивания слева).

Matteo Italia 20.12.2018 21:33

@MatteoItalia, более формально intro.execution, операнды оператора сложения неупорядочены

Jans 20.12.2018 21:38

Также на cppreference; с пункта 14 - модификации, введенные в C++ 17; они что-то особенное (и значительно больше, чем ответ, который я привел выше), но они не включают добавление.

Matteo Italia 20.12.2018 21:40

Что, если *Ptr ссылается на сам Ptr, приведет ли это к неопределенному поведению?

Bergi 20.12.2018 23:53

Re: «Такие выражения [как i = ++i + ++i] являются незаконными, поскольку, если не существует какого-либо оператора, который упорядочивает побочные эффекты, точный момент, в который они применяются, не определен, поэтому не определено, какие именно значения i примет в различных точках выражение ": Я не уверен, что согласен с этим объяснением. Это объяснение объяснило бы, почему поведение i = ++i + ++i было неопределенные, если бы это было так; но поведение полностью соответствует неопределенный, и это объяснение на самом деле не объясняет, почему.

ruakh 20.12.2018 23:56

@ruakh Не определено, потому что не может быть определено. В нем слишком много двусмысленности. Вопрос «Почему это не определено» возникает из-за того, что вы не можете его определить.

Nelson 21.12.2018 06:46

@ruakh: в конце концов, он не определен, потому что в стандарте указано, что он не определен; Хорошее обоснование состоит в том, что стандарт оставлял разработчикам так много возможностей для применения побочных эффектов всякий раз, когда и как бы они ни чувствовали себя лучше всего, что они прошли весь путь и оставили его полностью неопределенным, чтобы обеспечить любую возможную оптимизацию (скажем, сохранить половину значения в какой-то момент, а другая половина - позже, тем временем генерируя представление ловушки).

Matteo Italia 21.12.2018 07:59

Этот

++*Ptr++;

не вызывает U.B и оценивается как ++(*(Ptr++))

  • ptr++;/* address post incremented i.e doesn't change here itself */
  • *ptr;/* dereference same address i.e value at location where ptr earlier pointed i.e 6 */
  • ++*ptr;/* value changed where ptr pointed i.e Ar[0] becomes 7 */

Обратите внимание, что приращения поста Ptr++ оцениваются как

  • Ptr;/* Ptr doesn't change here itself in same expression */
  • Ptr = Ptr + 1;/* in next expression, Ptr considers the incremented one */

Другие вопросы по теме