Почему выражение a = a + b - ( b = a ) дает предупреждение о точке последовательности в С++?

Ниже приведен тестовый код:

int main()
{
    int a = 3;
    int b = 4;
    a = a + b - (b = a); 

    cout << "a :" << a << " " << "b :" << b << "\n";    
    return 0;
}

При компиляции выдается следующее предупреждение:

> $ g++ -Wall -o test test.cpp test.cpp: In function ‘int main()’:
> test.cpp:11:21: warning: operation on ‘b’ may be undefined
> [-Wsequence-point]

Почему операция может быть неопределенной?

Насколько я понимаю, сначала следует оценить подвыражение (b = a) из-за более высокого приоритета (), таким образом установив b = a. Затем, поскольку «+» и «-» имеют одинаковый приоритет, выражение будет оцениваться левоассоциативно. Таким образом, затем следует вычислить a + b, и, наконец, результат (b = a) следует вычесть из a + b. Я не вижу, чтобы здесь нарушалось какое-либо правило точки последовательности.


person gjain    schedule 09.11.2012    source источник
comment
Не помечайте что-либо одновременно тегами c и c++, если вопрос действительно не касается обоих языков.   -  person Dietrich Epp    schedule 10.11.2012


Ответы (4)


Существует разница между вычислением выражения и завершением его побочных эффектов.

Выражение присваивания b = a будет оцениваться перед вычитанием из-за более высокого приоритета скобок. В результате оценки будет предоставлено значение a. Однако запись этого значения в b может не завершиться до следующей точки последовательности, которая в данном случае является концом полного выражения. Таким образом, конечный результат общего выражения не определен, поскольку вычитание может принимать значение b до или после присваивания.

person Sergey Kalinichenko    schedule 09.11.2012
comment
Спасибо за кристально ясное объяснение. Существует разница между оцениваемым выражением и выполнением его побочных эффектов. Это попадает в яблочко. - person gjain; 18.11.2012
comment
Ваше второе предложение неверно. Скобки не имеют ничего общего с порядком вычисления. Например, в выражении f() + (g() + h()) три функции могут вызываться в любом из 6 возможных порядков. - person fredoverflow; 27.01.2014
comment
@FredOverflow Сначала я имел в виду перед вычитанием. Я исправил фразу. Спасибо! - person Sergey Kalinichenko; 27.01.2014
comment
@dasblinkenlight: Является ли поведение неопределенным в стандарте C ++ 11 и более поздних версий? - person Destructor; 16.11.2015
comment
@PravasiMeet Насколько я знаю, это остается UB в C++ 11 и последующих стандартах. - person Sergey Kalinichenko; 16.11.2015
comment
@dasblinkenlight: хорошо, понял. - person Destructor; 16.11.2015

В C++ подвыражения в арифметических выражениях не имеют временного порядка.

a = x + y;

Сначала оценивается x или y? Компилятор может выбрать либо то, либо другое. Порядок оценки не совпадает с приоритетом операций: приоритет операций строго определен, а порядок оценки определяется только степенью детализации, в которой ваша программа имеет точки последовательности.

На самом деле, на некоторых архитектурах можно создавать код, который одновременно оценивает и x, и y, например, архитектуры VLIW.

person Dietrich Epp    schedule 09.11.2012

Чтобы решить эту проблему, разделите их на два разных утверждения.

PS: Не забывайте, что люди могут ошибаться при выполнении арифметических операций. Поэтому лучше сделать операции более понятными, разделив их на разные операторы. Надеюсь, я помог.

int main() 
{
   int a = 3;
   int b = 4;

   /* Two different Statements*/
   b = a;

   /* or a = a + b - a */
   a = a + b - b; 

   cout<<"a :"<<a<<" "<<"b :"<<b<<"\n";    
   return 0;
}
person Alberto Bonsanto    schedule 09.11.2012
comment
-1. Хотя это очевидное решение проблемы, оно вообще не отвечает на вопрос. - person Daniel Kamil Kozar; 27.01.2014
comment
На самом деле это неправильно по 2 причинам. 1) Исходное выражение работает (вроде как), потому что это одно выражение. Если разделить на 2, то нет. В тот самый момент, когда вы запускаете b = a;, исходное значение b теряется навсегда. Если вы запустите это, вы получите a :3 b :3. 2) Ваше второе выражение a = a + b - b; сводится к a = a;, тогда как вы хотите a = b;. Вы можете исправить это, используя версию в вашем комментарии (a = a + b - a;), но она все равно не будет работать из-за проблемы номер 1. Кроме того, как указал Дэниел, это не то, что хотел знать ОП. - person Fabio says Reinstate Monica; 04.01.2016

a = b + a - a; просто записывается как

a = b + a - (b = a)------>> (опыт 1)

Следующие три результата такие же, как (exp 1) a = (b + a - (b = a)); a = ((b + a) - (b = a)); a = (b + a) - (b = a);

Наблюдения +, - операторы имеют одинаковый приоритет, а также ассоциативность слева направо. Следовательно, сначала выполняется «b + a», а затем значение «a» присваивается «b» перед вычитанием.

Теперь обратите внимание на следующее. Когда a = 10 и b = 20;

a = (b = a) - b + a; =======> a = 10; b = 10 a = ((b = a) - b + a); =======> a = 10; b = 10

a = ((b = a) - (b + a)); =======> а = -10; b = 10 Из приведенных выше выражений ясно, что даже если самая внутренняя скобка выполняется первой, сначала следует ассоциативность, а затем приоритет

Примечание. Чтобы избежать путаницы между приоритетом внешних и внутренних скобок, рассмотрим следующее выражение a = (b + a - (b = a)) =====> Фактический результат => a = 20, b = 10; было бы a = 10, b = 10; (если приоритет первичен по сравнению с ассоциативностью) Таким образом, на приведенном выше примере мы можем сказать, что ассоциативность первична по сравнению с приоритетом

person Ganesh Keerthi D G    schedule 07.09.2016
comment
Извините, но это не имеет смысла - person M.M; 07.09.2016