Возможный дубликат:
Вычисления неупорядоченных значений (точки последовательности a.k.a)
Неопределенные Точки Поведения и Последовательности
Приоритет оператора по сравнению с порядком оценки
Я все еще пытаюсь обернуть голову, как следующее выражение приводит к неопределенному поведению:
a = a++;
После поиска SO об этом я нашел следующий вопрос:
Разница между точками последовательности и приоритетом оператора? 0_о
Я прочитал все ответы, но у меня все еще есть трудности с деталями. Один из ответов описывает поведение моего приведенного выше примера кода как неоднозначное, с точки зрения того, как a
модифицируется. Например, это может сводиться к одному из следующих:
a=(a+1);a++;
a++;a=a;
Что именно делает a
модификация неоднозначная? Связано ли это с инструкциями процессора на разных платформах, и как оптимизатор может воспользоваться неопределенным поведением? Другими словами, это кажется неопределенным из-за сгенерированного ассемблера?
Я не вижу смысла использовать компилятор a=(a+1);a++;
это выглядит просто странно и не имеет особого смысла. Чем бы обладал компилятор, чтобы он так себя вел?
РЕДАКТИРОВАТЬ:
Просто чтобы быть понятным, я понимаю, что происходит, я просто не понимаю, как это может быть неопределенным, когда существуют правила приоритета операторов (которые по существу определяют порядок вычисления выражения). Назначение происходит последним в этом случае, поэтому a++
сначала нужно оценить, чтобы определить значение, которое нужно присвоить a
, Так что я ожидаю, что a
сначала изменяется во время приращения после исправления, но затем возвращает значение для a
(вторая модификация). Но правила приоритета операторов, кажется, делают поведение очень ясным для меня, я не могу найти, где есть какая-либо «комната для маневра», чтобы он имел неопределенное поведение.
Первый ответ в вопросе, с которым вы связаны объясняет точно, что происходит Я попытаюсь перефразировать это, чтобы сделать это более ясным.
Приоритет оператора определяет порядок вычисления значений с помощью выражений. Результат выражения (a++)
хорошо понято.
тем не мение, модификация переменной a
не является частью выражения. Да, действительно. Это та часть, которую вам трудно понять, но это просто, как это определяют C и C ++.
Выражения приводят к значениям, но некоторые выражения могут иметь побочные эффекты. Выражение a = 1
имеет значение 1
, но он также имеет побочный эффект установки переменной a
в 1
, Что касается того, как C и C ++ определяют вещи, это два разных шага. Так же, a++
имеет значение и побочный эффект.
Точки последовательности определяют, когда побочные эффекты видны для выражений, которые оцениваются после этих точек последовательности. Приоритет оператора не имеет ничего общего с точками последовательности. Вот как C / C ++ определяет вещи.
Это, вероятно, слишком упрощенное объяснение, но я думаю, что это потому, что нет способа решить, когда код «сделан» с «a». Это делается после приращения или присвоения? Резолюция заканчивается круговой Присвоение после приращения меняет семантику применения приращенного значения. То есть код не выполняется с помощью «a» до тех пор, пока «a» не будет увеличен, но a не будет увеличен до тех пор, пока не будет выполнено присваивание. Это почти языковая версия тупика.
Как я уже сказал, я уверен, что это не очень хорошее «академическое» объяснение, но именно так я и прячу это между моими собственными ушами. Надеюсь, это как-то полезно.
Правила приоритета определяют порядок, в котором оцениваются выражения, но побочные эффекты не должны возникать во время оценки. Они могут произойти в любое время до следующей точки последовательности.
В этом случае побочный эффект приращения не упорядочивается ни до, ни после присваивания, поэтому выражение имеет неопределенное поведение.
Дело в том, что на некоторых архитектурах ЦП, таких как Intel Itanium, эти две операции могут быть распараллелены компилятором на уровне команд, но если сделать вашу конструкцию четкой, это будет запрещено. На момент спецификации точки последовательности эти архитектуры были в основном гипотетическими, и, поскольку Itanium был провалом, можно утверждать, что по состоянию на 2012 год большая часть этого является ненужной сложностью в языке. В принципе, нет никаких возможных недостатков для любой архитектуры, которая все еще широко используется, и даже для Itanium преимущество в производительности было минимальным, а головная боль при написании компилятора, который мог бы даже использовать его, была огромной.
Также обратите внимание, что в C ++ 11 точки последовательности были заменены последовательными до и последовательными после, что сделало больше подобных ситуаций.
Это заявление a=a++
имеет два результата и два задания:
a=a
(потому что это postincrement) И
a=a+1
Эти назначения явно приводят к другому окончательному значению для a
,
Составители стандарта c не указали, какое из двух заданий должно быть написано a
во-первых, и во-вторых, поэтому авторы компиляторов могут свободно выбирать, что им нравится, в любой конкретной ситуации.
В результате он (это конкретное утверждение) не будет аварийно завершаться, но ваша программа больше не может полагаться на наличие определенного значения.
Позвольте мне пройти через основную проблему в заявлении a = a++
, Мы хотим добиться всего следующего:
определить стоимость a
(возвращаемое значение a++
№ 1)
приращение a
(побочный эффект a++
№ 2)
присвоить старое значение a
(эффект от задания № 3)
Существует два возможных способа, которыми это можно упорядочить:
Хранить оригинал a
в a
(без операции); затем увеличить a
, Такой же как a = a; ++a;
, Это последовательность № 1- № 3- № 2.
оценивать a
, приращение a
присвоить исходное значение обратно a
, Такой же как b = a; ++a; a = b;
, Это последовательность № 1- № 2- № 3.
поскольку нет заданной последовательности, любая из этих операций допустима. Но у них другой конечный результат. Ни одна последовательность не является более естественной, чем другая.