Назначение и точки последовательности: как это неоднозначно?

Рассмотрим C-код a = a = a . Нет никакой точки последовательности для назначения, поэтому этот код создает предупреждение при компиляции об неопределенной операции на a .

Каковы возможные значения, которые могут иметь здесь? Похоже, что невозможно изменить значения. Есть ли вообще неопределенное поведение или компиляторы просто ленивы?

    Правила неопределенного поведения при нарушениях последовательности точек не делают исключения для ситуаций, когда «значение не может измениться». Никто не заботится о том, меняется ли стоимость или нет. Важно то, что когда вы делаете какой-либо доступ на запись к переменной, вы изменяете эту переменную. Даже если вы присваиваете переменной значение, которое оно уже имеет, вы все равно выполняете модификацию этой переменной. И если несколько модификаций не разделены точками последовательности, поведение не определено.

    Можно, вероятно, утверждать, что такие «модификации без модификации» не должны вызывать никаких проблем. Но спецификация языка не касается таких деталей. В терминологии языка, опять же, каждый раз, когда вы что-то пишете в переменную, вы ее изменяете.

    Более того, тот факт, что вы используете слово «двусмысленный» в своем вопросе, кажется, подразумевает, что вы считаете, что поведение неуточнено . Т.е. как в «результирующем значении переменной (или нет) неоднозначно». Тем не менее, при нарушениях последовательности символов спецификация языка не ограничивается утверждением, что результат не указан . Он идет намного дальше и объявляет поведение неопределенным . Это означает, что обоснование этих правил учитывает не просто непредсказуемое конечное значение некоторой переменной. Например, на какой-то мнимой аппаратной платформе непересекающаяся модификация может привести к недействительному коду, генерируемому компилятором, или тому подобному.

    На самом деле это неопределенное поведение. a может иметь любую ценность вообще. «Я не могу думать о том, как это может сломаться» – это не то же самое, что «он гарантированно работает».

    Фактически вся программа имеет «неопределенное поведение» после выполнения этого оператора. Речь идет не только о значении a – программа может что- либо сделать, в том числе идти в бесконечный цикл, печатать вывоз мусора или сбой.

    «Неопределенное поведение» на самом деле просто означает, что стандарт C больше не ограничивает того, что делает программа. Это не мешает вам рассуждать о том, как конкретный компилятор может вести себя, когда видит этот код, но он все еще не является допустимой программой на C, и об этом предупреждает компилятор.

     int a = 42; a = a = a; 

    это неопределенное поведение.

    Правила точки последовательности были написаны для облегчения работы разработчиков компиляторов.

    В стандарте C нет правила, которое гласит: «Если поведение будет неоднозначным, тогда поведение не определено». В фактическом правиле в выпуске C 1999 говорится: «Между предыдущей и следующей точкой последовательности объект должен иметь измененное значение не более одного раза оценивая выражение. Кроме того, предыдущее значение должно считываться только для определения значения, которое необходимо сохранить ».

    Ваш код нарушает это правило: он изменяет значение a . (В примечании в 3.1 3 говорится, что «Изменить» включает случай, когда новое значение, хранящееся, совпадает с предыдущим значением.)

    Вот и все. Не имеет значения, можете ли вы понять однозначную интерпретацию этого кода. Дело только в том, что оно нарушает правило. Поскольку это нарушает правило, поведение не определено.

    В C 2011 году правило указано более техническим образом. 6.5 2 говорит: «Если побочный эффект скалярного объекта не зависит от другого побочного эффекта для одного и того же скалярного объекта или вычисления значения с использованием значения одного и того же скалярного объекта, поведение не определено. Если существует несколько допустимых порядков подвыражений выражения, поведение не определено, если такой какой-либо побочный эффект имеет место в любом из упорядочений ». Когда оператор присваивания сохраняет значение в объекте, это на самом деле побочный эффект . (Основной эффект заключается в том, что он оценивает значение, которое хранится.) Таким образом, это правило в C 2011 гласит в основном то же самое, что и правило C 1999: у вас может не быть двух побочных эффектов для одного и того же объекта.

    Вполне вероятно, что вы получите желаемое поведение. Когда кто-то пишет a=a=a он, вероятно, хочет, чтобы он оставался неизменным, и когда он пишет a=a=b он, вероятно, хочет, чтобы к концу инструкции было изменено на b .

    Однако есть мыслимые комбинации аппаратного и программного обеспечения, которые действительно нарушают это предположение. Рассмотрим, например, аппаратное обеспечение, где у вас есть явный параллельный stream команд. Затем двойное назначение можно скомпилировать в две инструкции, которые пытаются хранить данные одновременно в одном и том же регистре. Кроме того, разработчик аппаратного обеспечения мог также сделать предположение, что пары инструкций, которые это делают, не допускаются и могут использовать значения don’t-care для этих случаев (и упростить HW).

    Тогда вы действительно можете оказаться в ситуации, когда a=a=a самом деле изменяет значение a и a=a=b заканчивается в не равном b .