10.2 Porządek wartościowania

Wyrażenia mogą zawierać podwyrażenia, które trzeba obliczyć przed wykonaniem operacji. Na przykład może to być wywołanie funkcji

       int k = fun1(x) + fun2(y);
Aby wykonać dodawanie, trzeba wywołać funkcje, które obliczą wartości argumentów tego dodawania. Ale czy najpierw zostanie wywołana funkcja fun1 czy fun2? Czyli właśnie: jaki jest porządek wartościowania? Otóż w takich przypadkach nie jest on określony przez standard języka (tak jak jest określony w Javie). Najlepiej więc pisać program tak, aby wynik nie zależał od tego porządku. W przeciwnym przypadku wynik ten może być zaskakujący. Na przykład, co wypisze następujący program?


P64: porz.cpp     Porządek wartościowania

      1.  #include <iostream>
      2.  using namespace std;
      3.  
      4.  int zzz;     // globalne, inicjowane zerem
      5.  
      6.  int fun1() {
      7.      return zzz += 1;
      8.  }
      9.  
     10.  int fun100() {
     11.      return zzz += 100;
     12.  }
     13.  
     14.  int main() {
     15.      cout << fun1() << " " << fun100() << endl;  
     16.  }

Wydawać by się mogło, że w linii  najpierw wywołana zostanie funkcja fun1. Dodaje ona do wartości globalnej zmiennej zzz jedynkę i zwraca tę podwyższoną wartość. Ponieważ zzz jako zmienna globalna jest zerowana natychmiast po utworzeniu, wywołanie fun1 powinno zwrócić 1. Natomiast następujące po nim wywołanie funkcji fun100 dodaje do wartości zzz liczbę 100 i zwraca otrzymaną wartość, która wobec tego powinna wynosić teraz 101. Tymczasem rezultat uruchomienia tego programu to

    101 100
A zatem, najpierw wywołana została fun100 i jej wynik zapamiętany na jakimś wewnętrznym stosie, zmienna zzz otrzymała wartość 100; następnie wywołana została funkcja fun1, która wobec tego zwróciła 101, co zostało wstawione do strumienia, po czym do tego strumienia została wstawiona zapamiętana wartość zwrócona przez fun100.

Z podobnych powodów należy unikać odwoływania się w instrukcji dwa razy do zmiennej, która w tej samej instrukcji ulega zmianie; np. zapis

       tablica[i] = ++i;
jest mylący i może być różnie interpretowany: czy po lewej stronie podczas określania adresu pod jaki należy wpisać wynik przypisania wartość indeksu i jest już zwiększona, czy jeszcze nie? (Powinna być już zwiększona, ale lepiej takich konstrukcji nie stosować.)

T.R. Werner, 23 lutego 2019; 23:59