Порядок оценки языка C

  1. x+=x*=x Неопределенное поведение?
  2. Может ли кто-нибудь объяснить это правило в порядке оценки? Что такое «разовая оценка»? Что противоположно «единичной оценке»?
    # P1 #

person Patrick    schedule 20.11.2019    source источник
comment
Is x+=x*=x undefined behavior? Да, нет точки последовательности между множественным назначением (т. Е. Изменением значения).   -  person Sourav Ghosh    schedule 20.11.2019
comment
Пожалуйста, не предоставляйте ссылки на C ++ для вопросов C. Это разные языки.   -  person Andrew Henle    schedule 20.11.2019
comment
@SouravGhosh Я не уверен: A Составное присваивание в форме E1 op = E2 эквивалентно простому выражению присваивания E1 = E1 op (E2). Итак, я прочитал, что это означает, что оно должно оцениваться как x = x + ( x = ( x * x ) ). Я не вижу UB.   -  person Andrew Henle    schedule 20.11.2019
comment
Итак, что нам нужно объяснить? C? или C ++? Что такое x, какой язык программирования ?!   -  person Antti Haapala    schedule 20.11.2019
comment
Да, x += x *= x эквивалентно x += (x *= x), поскольку += и *= имеют равный приоритет и ассоциативность справа налево. Однако это не гарантирует, что x * = x оценивается перед крайним левым x. В любом случае поведение в C не определено, поскольку x изменяется дважды в одном операторе.   -  person Peter    schedule 20.11.2019
comment
@AndrewHenle, это UB, потому что в любом случае есть несколько назначений. Неважно, как вы считаете группировку.   -  person Antti Haapala    schedule 20.11.2019
comment
@AndrewHenle Каким должно быть окончательное значение X для вас самих? Не то, что я ожидал: ideone.com/ncXE45 :) Итак, UB это ...   -  person Eugene Sh.    schedule 20.11.2019
comment
@AnttiHaapala Я ищу ссылку сейчас ...   -  person Andrew Henle    schedule 20.11.2019
comment
@AndrewHenle как всегда: Между предыдущей и следующей точкой последовательности объект должен иметь свое сохраненное значение, измененное не более одного раза путем оценки выражения. Кроме того, предыдущее значение должно быть прочитано только для определения значения, которое будет сохранено   -  person Antti Haapala    schedule 20.11.2019
comment
Ясно, что это не определено. x изменяется дважды между точками последовательности. Почему по этому поводу возникают какие-либо сомнения или споры?   -  person Steve Summit    schedule 20.11.2019
comment
@Patrick Внизу страницы, на которую вы указали, есть ссылка на собственную версию C страницы.. Это может быть лучшим выбором для вашего вопроса.   -  person Blastfurnace    schedule 20.11.2019
comment
Ну, это не UB в C ++ 17 ... C и C ++ одинаковы до C11 / C ++ 11. В C ++ 14 комитету C ++ удалось только запутаться. C ++ 17 содержит исправление, которое должен был содержать C ++ 14. Что касается C, комитет не хотел бы улучшать язык, удаляя хорошо известные языковые недостатки.   -  person Lundin    schedule 20.11.2019
comment
Для этого действительно нужен тег языкового юриста. Не имеет значения, является ли это неопределенным поведением; это злодеяние, о котором никогда не следует писать, кроме как с целью языкового юриспруденции!   -  person William Pursell    schedule 20.11.2019
comment
@AnttiHaapala Прошу прощения за ошибку при копировании ссылки. Это c ++, спасибо, что поправили ссылку.   -  person Patrick    schedule 21.11.2019


Ответы (2)


x+=x*=x имеет неопределенное поведение, потому что x назначается дважды между точками последовательности.


Текст, соответствующий 14) в C11, C17 говорит

Составное присваивание формы E1 op = E2 эквивалентно простому выражению присваивания E1 = E1 op (E2), за исключением того, что lvalue E1 оценивается только один раз, а в отношении вызова функции с неопределенной последовательностью операций составное присвоение - это единственная оценка.

Я считаю, что это означает, что

int x = 0;

int foo(void) {
    x = 5;
    return x;
}

int main(void) {
    int y = foo() + (x += 2); 
}

будет иметь либо поведение

int main(void) {
    int _tmp = x += 2;
    int y = foo() + _tmp; 
}

or

int main(void) {
    int _tmp = foo();
    int y = _tmp + (x += 2); 
}

и не может быть разделен, например, на

int main(void) {
    int _tmp = x;
    int _tmp2 = foo();
    x = _tmp + 2;
    int y = _tmp2 + x; 
}

Обратите внимание, что эта гарантия является новой для C11 и отсутствует в C89, C99.

person Antti Haapala    schedule 20.11.2019
comment
Учитывая что-то вроде *p += f();, будет ли в Стандарте ясно, может ли вызов произойти между вычислением p и доступом к этому lvalue? Я думаю, что стандарты C и C ++ выиграют, если признают, что значение lvalue, используемое в качестве левого операнда оператора присваивания или для которого был выбран его адрес, должно быть разрешено до присвоения или первого использования полученного адреса, а также явно определяя, как такое разрешение упорядочивается с другими частями оценки выражения. - person supercat; 22.11.2019

1) Да.

C17 6.5.16 Операторы присваивания, §3

Вычисления операндов не упорядочены.

Что делает его UB из-за C17 6.5 §2

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

Каждое задание - это побочный эффект. У C99 было то же правило, но его было намного легче понять:

C99 6.5 §2

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


2) Весь текст с изменениями последовательности C11 довольно запутан для чтения. Они используют термины, которые нигде официально не определены, такие как «единичная оценка».

Нормативный текст: C17 6.5.15.2 §3.

Составное присваивание формы E1 op = E2 эквивалентно простому выражению присваивания E1 = E1 op (E2), за исключением того, что lvalue E1 оценивается только один раз, а в отношении вызова функции с неопределенной последовательностью операций составное присвоение - это единственная оценка.

Я полагаю, что это просто означает, что такая операция, как int x; ... x += 1, должна привести к машинному коду, например:

  • Сохранить 1 в регистре
  • Добавьте x для регистрации
  • Записать регистр в x

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

person Lundin    schedule 20.11.2019
comment
Я предполагаю, что в нем говорится, что z = foo() + (x += y) foo либо увидит, что x уже изменен, и в этот момент foo() путем изменения глобального x не может повлиять на значение оценки x += y, либо foo() выполняется полностью раньше, а x += y оценивается. - person Antti Haapala; 20.11.2019
comment
Я думаю, что составное присваивание в этом контексте является атомарным, и его нельзя чередовать с другими функциями. - person Patrick; 21.11.2019