Я постараюсь выделить разницу между тем, что вы считаете порядком оценки , и тем, что компилятор считает.
Математически мы говорим, что в выражении a + b * c
умножение вычисляется перед сложением. Конечно, это должно быть потому, что мы должны знать, что добавить к a
.
Однако компилятору не обязательно нужно учитывать выражение b * c
, прежде чем он вычислит a
. Вы можете подумать, что, поскольку умножение имеет более высокий приоритет, компилятор сначала будет смотреть на эту часть выражения. На самом деле, нет никакой гарантии относительно того, что компилятор решит сделать первым. Он может сначала оценить a
, либо b
, либо c
. Это поведение не указано по стандарту.
Чтобы продемонстрировать, давайте посмотрим на следующий код:
#include <iostream>
int f() { std::cout << "f\n"; return 1; }
int g() { std::cout << "g\n"; return 2; }
int h() { std::cout << "h\n"; return 3; }
int main(int argc, const char* argv[])
{
int x = f() + g() * h();
std::cout << x << std::endl;
return 0;
}
Каждая функция, f()
, g()
и h()
, просто выводит имя функции на стандартный вывод и затем возвращает 1, 2 или 3 соответственно.
Когда программа запускается, мы инициализируем переменную x
, которая будет f() + g() * h()
. Это именно то выражение, на которое мы смотрели ранее. Ответ, конечно, будет 7. Теперь, наивно, вы можете предположить, что умножение произойдет первым, поэтому оно пойдет туда и будет делать g()
, затем умножить его на h()
, затем оно будет делать f()
и добавьте его к предыдущему результату.
На самом деле, компиляция с помощью GCC 4.4.5 показывает, что функции выполняются в том порядке, в котором они появляются в выражении: f()
, затем g()
, затем h()
. Это не то, что обязательно произойдет одинаково во всех компиляторах. Это полностью зависит от того, как компилятор хочет это сделать.
Если вы выполняете операции, которые являются ассоциативными или коммутативными, то компилятор также может свободно переключаться между математическими группировками в выражении, но только если , результат будет точно тот же самый. Компилятор должен быть осторожен, чтобы не делать никаких перегруппировок, которые могут вызвать переполнение, которое в любом случае не произошло бы. Пока результат соответствует стандарту, компилятор может делать то, что ему нравится.