11.3 Wywołanie funkcji

Wywołanie funkcji ma postać nazwy tej funkcji z podaną w nawiasach okrągłych listą oddzielonych przecinkami argumentów. Oczywiście liczba argumentów wywołania musi być dokładnie taka jak liczba parametrów w deklaracji i definicji funkcji. Nawet jeśli funkcja jest bezargumentowa, nawiasy musimy podać: w przeciwnym przypadku nazwa zostanie potraktowana nie jako wywołanie, ale jak nazwa wskaźnika wskazującej funkcję (więcej o wskaźnikach funkcyjnych powiemy w osobnym podrozdziale na ich temat ).

Rolę argumentów mogą pełnić dowolne wyrażenia, których wartość jest typu zgodnego z zadeklarowanym typem odpowiedniego parametru funkcji. Nie musi być to typ identyczny z typem parametru: wywołanie będzie prawidłowe, jeśli wartość argumentu można niejawnie przekształcić na wartość zadeklarowanego typu parametru (patrz podrozdział o konwersjach standardowych ). Jeśli przy takiej konwersji może być tracona informacja (czyli jest to konwersja zawężająca, a nie promocja), to zazwyczaj podczas kompilacji otrzymamy ostrzeżenia.

Jeśli funkcja zwraca rezultat, to ostatnią wykonywaną instrukcją musi być zawsze instrukcja return zwracająca wartość typu, jaki został zadeklarowany jako typ funkcji, albo typu, który może być niejawnie przekształcony do typu funkcji.

Jeśli funkcja jest bezrezultatowa, to instrukcja return może (choć nie musi) w niej występować — jeśli występuje, to nie może zawierać żadnego wyrażenia, którego wartość miałaby być zwrócona. Dla funkcji bezrezultatowych domniemywa się instrukcję return tuż przed nawiasem kończącym definicję ciała tej funkcji.

Proces wywoływania możemy sobie wyobrażać tak (szczegóły mogą zależeć od platformy i od kompilatora):

Rozpatrzmy przykład ilustrujący konwersje argumentów i rezultatu funkcji:


P64: function.cpp     Konwersje argumentów i wartości zwracanej

      1.  #include <iostream>
      2.  using namespace std;
      3.  
      4.  int d2i(double);
      5.  double i2d(int);
      6.  
      7.  int main() {
      8.      double x = 3.5;
      9.  
     10.      cout << "d2i(x) = " << d2i(x) << endl
     11.           << "i2d(x) = " << i2d(x) << endl;
     12.  }
     13.  
     14.  int d2i(double x) { return 3*x; }
     15.  double i2d(int k) { return 3*k; }

Obie funkcje, d2ii2d, zwracają wartość otrzymanego argumentu pomnożoną przez trzy. Wynikiem powinna być liczba 10,5. Kompilator wypisał ostrzeżenia, ale program skompilował się i uruchomił:

    cpp> g++ -o function function.cpp
    function.cpp: In function `int main()':
    function.cpp:11: warning: passing `double' for converting 1
                     of `double i2d(int)'
    function.cpp: In function `int d2i(double)':
    function.cpp:14: warning: converting to `int' from `double'
    cpp> ./function
    d2i(x) = 10
    i2d(x) = 9
    cpp>
Jak widać, obie funkcje zwracają inne rezultaty, jednak żadnego z nich nie uznalibyśmy za prawidłowy! Dlaczego tak się stało?

Parametr funkcji d2i jest typu double. Zatem wartość zmiennej x, która też jest typu double, została przekazana do funkcji jako double. W funkcji wartość ta została pomnożona przez trzy; wynikiem jest 10,5. Ale typem funkcji (typem jej wartości zwracanej) jest typ int, zatem przed zwróceniem wynik jest przekształcany do tego typu. Część ułamkowa jest więc „obcinana” i zwrócona wartość to liczba całkowita 10 (typu int).

W przypadku funkcji i2d parametr jest typu int. Zatem wartość argumentu, czyli 3,5, zostanie przekształcona do typu int i będzie wynosić 3. Po pomnożeniu przez 3 wynik będzie całkowity i równy 9. Funkcja jest typu double, więc przed zwróceniem wynik zostanie przekształcony do tego właśnie typu; tym niemniej jego wartość pozostanie 9.

Zauważmy, że w powyższym programie definicje obu funkcji umieściliśmy po funkcji main. Na początku programu są one jednak zadeklarowane, a zatem kompilator zna ich prototyp, gdy napotyka ich użycie wewnątrz funkcji main.

T.R. Werner, 25 lutego 2017; 22:31