|
|
От: |
LaptevVV
|
|
| Дата: | 07.07.04 05:02 | ||
| Оценка: | 2 (2) | ||
Как указано в стандарте языка С++, порядок вычисления подвыражений внутри выражений не определен. Не стоит полагаться на то, что вычисления выражений выполняются слева направо. Это не противоречит приоритету операций. Пусть у нас в программе написано выражение,
a = b + c + d;
где b, c и d сами являются выражениями. Операция сложения действительно будут выполняться слева направо, то есть сначала к b прибавится с, а потом к результату прибавится d. Однако порядок вычислений выражений b, c и d — не определен. Компилятор может сначала вычислить с, затем d и только потом b. Собственно, ну и пусть, скажете вы. Однако не все так просто. Рассмотрим несколько искусственный, но простой пример:
int i = 1, k = 2, j = 3; i = (i=j-k) + (i=j+k); cout << i << endl;
Ничего «криминального» не наблюдается, системы фирмы Borland (Borland C++ 3.1, Borland C++ 5, Borland C++ Builder 6) во всех режимах трансляции выдают один и тот же результат 6. Поменяв местами слагаемые
i = (i=j+k) + (i=j-k);
тоже во всех вариантах получаем 6. Вроде бы все логично: независимо от порядка вычисления выражение j-k должно иметь значение 1, а j+k равно 5. Поэтому результат получается равным 6. Если бы я писал транслятор, я именно так и рассуждал бы при реализации арифметических выражений. Однако разработчики Visual C++ 6 думали иначе: то же выражение
i = (i=j-k) + (i=j+k);
в этой среде имеет значение 10. А если поменять слагаемые местами, тогда результат получается равным 2. Такие ситуации в стандарте называются undefined behaviour — неопределенное поведение. Основной отличительной чертой является неоднократное присваивание одной и той же переменной в пределах одного выражения. Например, следующее выражение является как раз таким:
(a+=b)+=a;
Какое значение переменной а будет добавляться к результату в скобках: то, которое было до изменения а или уже измененное? Неопределенное поведение может проявляться не только в операциях с присваиванием, но и при использовании инкремента или декремента. Например, оператор
i = ++i + 1;
является примером из стандарта, демонстрирующим неопределенное поведение. Вообще-то говоря, невинные, на первый взгляд, выражения
++++i; (++i)++;
тоже отличаются свойством неоднократного присваивания, однако во всех системах, где я их испытывал, получался один и тот же результат — увеличение i на 2. С другой стороны, оператор
i = 7, i++, i++;
в стандарте указан как пример defined behaviour — определенное поведение. Переменная i в данном случае равна 9. Таким образом, операция запятая может использоваться для «придания определенности» вычислениям. Другими операции, гарантирующими определенный порядок вычисления выражений являются логические операции И (||) и ИЛИ (&&).
Таким образом, многие вычисления необходимо явно проверять в той среде, с которой вы в данный момент работаете.