« Funkcje, a słowo kluczowe return, lekcja »
Rozdział 20. Jak działa słowo kluczowe return w funkcjach i jakie są praktyczne zalety z właściwego używania wspomnianego słowa kluczowego. (lekcja)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
Autor: Piotr Szawdyński
Kurs C++

Funkcje, a słowo kluczowe return

[lekcja] Rozdział 20. Jak działa słowo kluczowe return w funkcjach i jakie są praktyczne zalety z właściwego używania wspomnianego słowa kluczowego.
Funkcje są potężne. Funkcje są przyjemne. Funkcje są pożyteczne. Funkcje są wygodne. Tak właśnie jest i dlatego powinniśmy starać się poznać je jak najlepiej. Tym razem przyjrzymy się bliżej słowu kluczowemu return, które ma swoje bardzo fajne oblicze o którym nic nie wiedziałem przez bardzo długi czas zajmowania się programowaniem w C++. Co to jest takiego? Zaraz się o tym dowiesz :)

Co wiemy do tej pory o słowie kluczowym return?

Na chwilę obecną o słowie kluczowym return powinieneś wiedzieć tyle, że może występować wewnątrz ciała funkcji i potrafi ono zwrócić wartość, którą da się odczytać w miejscu gdzie funkcja została wywołana. Z tym się nic nie zmienia i zmieniać nie będzie.

Słowo kluczowe return - nowe oblicze

Skoro przypomnieliśmy sobie co wiemy o słowie kluczowym return, zobaczmy teraz co ono jeszcze potrafi:
  • słowo kluczowe return może występować wewnątrz funkcji więcej niż raz;
  • słowo kluczowe return może występować w funkcjach, które nie zwracają wartości.

Jedna funkcja, wiele wystąpień return

Pierwszą nową sprawą, którą omówimy jest funkcja, w której występuje więcej niż raz słowo kluczowe return. Pisząc funkcje, które zwracają wartość często się zdarza, że na pewnym etapie pisanego kodu wiemy z góry jaki ma zwrócić wynik operacji, a w związku z tym chcielibyśmy ją przerwać, zwracając jednocześnie interesujący nas wynik. Słowo kluczowe return wychodzi natychmiastowo z funkcji zwracając jednocześnie wartość jaka zostanie za nim podana. Kod, który się znajdował w dalszej części funkcji nie zostanie już wykonany, bo i nie ma po co skoro funkcja obliczyła wynik, który chcieliśmy otrzymać. Przejdźmy teraz do przykładu, który zilustruje w praktyce to co zostało wyżej opisane:
C/C++
bool czyGodzinaPoprawna( int godz, int min, int sek )
{
    std::cout << "Jestem w funkcji czyGodzinaPoprawna" << std::endl;
    if( godz < 0 || godz > 23 )
         return false;
   
    std::cout << "Godzina jest poprawna" << std::endl;
    if( min < 0 || min > 59 )
         return false;
   
    std::cout << "Godziny i minuty sa poprawne" << std::endl;
    if( sek < 0 || sek > 59 ) return false;
   
    std::cout << "Czas jest poprawny" << std::endl;
    return true;
}

Funkcja wypisuje komunikaty po to byś miał lepiej zobrazowany temat, który teraz poznajesz - w praktyce są one zbędne i nie powinno ich tam być.

Jak widać mamy funkcję, która sprawdza czy czas podany poprzez argumenty funkcji jest poprawny. Jeżeli czas jest poprawny to funkcja powinna zwrócić nam prawdę, czyli true. W przeciwnym wypadku ma zwrócić fałsz tj. false.

Przykładowe wywołania i ich wyniki

Zobaczmy teraz co otrzymamy na ekranie w wyniku wywołania funkcji z różnymi argumentami:
  • Wywołanie
    czyGodzinaPoprawna( 24, 30, 50 );
    :
    Standardowe wyjście:
    Jestem w funkcji czyGodzinaPoprawna
  • Wywołanie
    czyGodzinaPoprawna( 12, 88, 50 );
    :
    Standardowe wyjście:
    Jestem w funkcji czyGodzinaPoprawna
    Godzina jest poprawna
  • Wywołanie
    czyGodzinaPoprawna( 1, 00, 99 );
    :
    Standardowe wyjście:
    Jestem w funkcji czyGodzinaPoprawna
    Godzina jest poprawna
    Godziny i minuty sa poprawne
  • Wywołanie
    czyGodzinaPoprawna( 7, 33, 01 );
    :
    Standardowe wyjście:
    Jestem w funkcji czyGodzinaPoprawna
    Godzina jest poprawna
    Godziny i minuty sa poprawne
    Czas jest poprawny

Praktyczny przykład

Zobaczmy teraz jak można w praktyce wykorzystać funkcję, która została zaprezentowana powyżej - funkcja została okrojona z komunikatów, które wypisywała:
C/C++
#include <iostream>

bool czyGodzinaPoprawna( int godz, int min, int sek )
{
    if( godz < 0 || godz > 23 )
         return false;
   
    if( min < 0 || min > 59 )
         return false;
   
    if( sek < 0 || sek > 59 )
         return false;
   
    return true;
}

void komunikatCzasu( bool bWynik )
{
    if( bWynik )
         std::cout << "Czas jest poprawny" << std::endl;
    else
         std::cout << "Czas jest niepoprawny" << std::endl;
   
}

int main()
{
    komunikatCzasu( czyGodzinaPoprawna( 24, 30, 50 ) );
    komunikatCzasu( czyGodzinaPoprawna( 12, 88, 50 ) );
    komunikatCzasu( czyGodzinaPoprawna( 1, 00, 99 ) );
    komunikatCzasu( czyGodzinaPoprawna( 7, 33, 01 ) );
    return 0;
}
Standardowe wyjście programu:
Czas jest niepoprawny
Czas jest niepoprawny
Czas jest niepoprawny
Czas jest poprawny

Słowo kluczowe return w funkcjach niezwracających wartości

Skoro już wiemy co potrafi słowo kluczowe return to zajmijmy się już formalnościami związanymi z funkcjami, które nie zwracają wartości. Słowo kluczowe return może występować w takich funkcjach. Pełni ono taką samą rolę jak w funkcjach, które zwracają wartość z tą różnicą, że wartości żadnej nie zwraca. Skoro funkcja nie zwraca żadnej wartości to i za słowem kluczowym return również nie umieszczamy żadnej wartości. W praktyce więc może to wyglądać tak:
C/C++
#include <iostream>

void wyswietl( int tablica[], int ile )
{
    if( ile <= 0 )
    {
        std::cout << "Tablica jest pusta." << std::endl;
        return;
    } //if
   
    int i = 0;
    do
    {
        std::cout << tablica[ i ] << ", ";
        i++;
    } while( i < ile );
   
    std::cout << std::endl;
}

int dopisz( int iLiczba, int tablica[], int ile )
{
    tablica[ ile ] = iLiczba;
    ile++;
    return ile; //zwraca ile jest elementów po dodaniu nowego
}

int main()
{
    int liczbaElementow = 0;
    int tablicaLiczb[ 10 ];
    wyswietl( tablicaLiczb, liczbaElementow );
   
    liczbaElementow = dopisz( 123, tablicaLiczb, liczbaElementow );
    wyswietl( tablicaLiczb, liczbaElementow );
   
    liczbaElementow = dopisz( 321, tablicaLiczb, liczbaElementow );
    wyswietl( tablicaLiczb, liczbaElementow );
    return 0;
}
Wyjaśnień więcej myślę, że tu nie potrzeba, więc sprawę uważam za zamkniętą :) Przeanalizuj jednak dobrze powyższy przykład - zawiera on bardzo ciekawą obsługę dodawania elementów do tablicy, która z pewnością jest dla Ciebie dobrym elementem edukacyjnym.

Praktyczne zastosowania

O ile zastosowanie słowa kluczowego return jest wygodne w sytuacjach, które zostały opisane o tyle jest ono niezastąpione w momencie gdy mamy funkcję, w której mamy zagnieżdżone pętle i wewnątrz nich w pewnym momencie stwierdzimy: 'Okej, wynik już znam - wychodzimy z tej funkcji'. Robienie warunków po to by wyjść z tych zagnieżdżonych pętli to byłaby droga przez mękę, więc praktycznie zawsze w takich sytuacjach wykorzystuje się omówioną własność słowa kluczowego return.

Zwracanie wartości przez funkcję

Pamiętaj, że funkcja, która zwraca wartość musi zwracać ją zawsze. Jeżeli funkcja dojdzie do końca bloku funkcji i nie napotka słowa kluczowego return zachowanie wyjścia z funkcji jest niezdefiniowane. Oznacza to, że aplikacja może zakończyć pracę poprzez wystąpienie błędu krytycznego aplikacji. Niektóre kompilatory łagodniej się w tych sytuacjach obchodzą i zwracają one wówczas przypadkową wartość.

Przykład niebezpiecznej funkcji

Załóżmy teraz, że napiszemy funkcję w której nie wszystkie możliwe ścieżki przejścia będą zwracały wartość:
C/C++
int funkcja( int liczba )
{
    if( liczba > 10 )
         return 0;
   
}
Próba kompilacji się powiedzie, jednak powinno pojawić się ostrzeżenie o następującej treści:

Code::Blocks - MinGW 4.4.1

In function 'int funkcja(int)':
warning: control reaches end of non-void function

Visual C++ 2008

warning C4715: 'funkcja' : not all control paths return a value

Ostrzeżenie to informuje, że funkcja nie uwzględnia wszystkich możliwych ścieżek przejścia, co w konsekwencji może dla pewnych danych spowodować niezdefiniowane zachowanie kodu. Log kompilacji jest Twoim przyjacielem i nie należy przechodzić obok niego nigdy obojętnie. Czytaj więc go i staraj się pisać kod tak aby żadne ostrzeżenia nie pojawiały się w logu kompilacji.

A co gdy nie dostałeś ostrzeżenia dla przykładowego kodu?

Taka sytuacja również może się zdarzyć. Wówczas należy wejść w opcje kompilatora i włączyć odpowiednią opcję.

Code::Blocks

  • W menu wybrać pozycję Settings;
  • następnie Compiler and debugger;
  • Zaznaczyć opcję: Enable all compiler warnings (overrides every other setting) [-Wall]
  • Zatwierdzić przyciskiem OK

Visual C++

W przypadku Visual C++ zalecam zwiększyć poziom ostrzeżeń z 3 stopnia na 4 stopień. Użytkownicy Visual C++ powinni sobie z tym poradzić - niniejszy kurs jest poświęcony Code::Blocks, więc szczegóły zostaną w tym przypadku pominięte.
Poprzedni dokumentNastępny dokument
Przekazywanie tablic jednowymiarowych do funkcjiLosowanie bez powtórzeń