Instrukcje continue i goto w C++. Obsługa pętli i etykiet w programach C++

Instrukcja continue - podstawy

Instrukcja continue podobnie jak instrukcja break jest związana z pętlami i podobnie jak instrukcję break można ją stosować zarówno w przypadku pętli for, while jak i do while. Różnicą jest to, że instrukcji tej nie stosujemy gdy używamy instrukcji switch (już wkrótce dowiesz się czym jest instrukcja switch).

Instrukcja continue powoduje przerwanie wykonania bieżącego kroku pętli i przejście do następnego kroku. Instrukcja continue działa tylko na pętlę w której się bezpośrednio znajduje - nie da się spowodować przerwania wykonania kroku pętli zewnętrznej.

Zachowanie instrukcji continue, ilustruje poniższy prosty program:

#include <iostream>

using namespace std;

int main()
{
  for (unsigned int i=0;i<=12;++i)
  {
     if ((i%3)==0)
        continue;
     cout <<"Liczba "<<i<<" nie jest podzielna przez 3"<<endl;
  }
 
  cout <<endl<<"Nacisnij ENTER aby zakonczyc..."<<endl;
  getchar();
  return 0;  
}
program nr 18.1

Jak widzisz, nic szczególnego w naszym przykładzie się nie dzieje. Po prostu jeśli liczba jest podzielna przez 3, zostanie wykonywana instrukcja continue, czyli przechodzimy do następnego kroku wywołania pętli. Sprawia to, że wszystkie instrukcje znajdujące się w dalszej części pętli nie są wykonywane (tutaj taką instrukcją jest wypisanie informacji na ekran).

Oczywiście w tym prostym przykładzie, można by było obejść się bez instrukcji continue poprzez zastosowanie instrukcji if, jednak nie zawsze skorzystanie z instrukcji if będzie możliwe lub wygodne - czasami po prostu lepiej wykorzystać nowo poznaną instrukcję.

Instrukcja continue - informacje dodatkowe

Instrukcja continue tak naprawdę nie jest instrukcją często używaną. Powiedziałbym, że jest używana bardzo rzadko i to w bardziej skomplikowanych programach/pętlach. Nie przedstawię Ci jednak takiego przykładu, gdyż nie ma to większego sensu, bowiem musiałbym zamieścić tu kilkaset linii kodu, aby zobrazować Ci sens użycia pętli continue, a niestety byłoby w tym programie zbyt wiele elementów, których jeszcze nie znasz.

Przedstawię Ci natomiast dwa proste przykłady, które zobrazują, że instrukcja continue po prostu upraszcza zapis i niekiedy właśnie z tego powodu warto z niej skorzystać.

#include <iostream>

using namespace std;

int main()
{
  for (unsigned int i=3;i<=17;++i)
  {
     if ((i%4)==0)
     {
        cout <<"Liczba ";
        cout <<i;
        cout <<" jest podzielna przez 4"<<endl;
     }
     else // dobrze ze if "miesci sie na ekranie" i wiem do czego jest ten else
     {        
        cout <<"Liczba ";
        cout <<i;
        cout <<" nie jest podzielna przez 4"<<endl;
     } // do czego jest ten nawias?
  } // a do czego ten?
  /*
    chyba bedzie trzeba uzyc komentarzy, bo niedlugo nie bede wiedziec o co tutaj
    chodzi
  */

 
  cout <<endl<<"Nacisnij ENTER aby zakonczyc..."<<endl;
  getchar();
  return 0;  
}
program nr 18.2

Oczywiście kod został dobrany celowo, aby przedstawić Ci problem. W tym celu mamy więcej instrukcji wypisania (chociaż można by było je zrealizować za pomocą jednej instrukcji). Jak widać z komentarzy, może być w pewnym momencie trudno zrozumieć, co się dzieje w programie.

Mamy dwa razy nawiasy klamrowe, więc wypadałoby je skomentować. Dodatkowo warto by też było skomentować do czego służy else - tutaj to jeszcze widać, bowiem po instrukcji if są tylko 3 instrukcje, ale co jeśli było by ich 20 albo 50?

Jak widzisz nawet w tak prostym programie, można się prędzej czy później pogubić, a nawet jeśli nie pogubić, to niepotrzebnie tracić czas na zrozumienie tego kodu.

Używając instrukcji continue, uda nam się w znacznym stopniu uprościć kod programu i uczynić go nieco łatwiejszym do zrozumienia. Oto ten sam program z użyciem instrukcji continue:

#include <iostream>

using namespace std;

int main()
{
  for (unsigned int i=3;i<=17;++i)
  {
     if ((i%4)==0)
     {
        cout <<"Liczba ";
        cout <<i;
        cout <<" jest podzielna przez 4"<<endl;
        continue;
     }
     // nie ma else - problem komentarza zniknal
     cout <<"Liczba ";
     cout <<i;
     cout <<" nie jest podzielna przez 4"<<endl;      
  } // tylko jeden nawias - wiec latwo zauwazyc ze dotyczy petli for
 
  cout <<endl<<"Nacisnij ENTER aby zakonczyc..."<<endl;
  getchar();
  return 0;  
}
program nr 18.3

Mam nadzieję, że również dla Ciebie powyższy program wygląda prościej od tego poprzedniego. Przede wszystkim nie ma części else, więc nie będziemy musieli stosować komentarzy, nawet jeśli w części if będzie wiele instrukcji.

Dodatkowo nie pojawiają się już 2 nawiasy klamrowe obok siebie, więc komentarze nie są już konieczne (chociaż oczywiście i tak komentarz może się przydać).

Podsumowując część tej lekcji dotyczącą instrukcji continue, ważne jest, aby udało Ci się zapamiętać, że taka instrukcja w ogóle istnieje i do czego w przybliżeniu służy.

Jak już wspomniałem i tak nie będziesz z tej instrukcji zbyt często korzystać. Kiedyś jednak nadejdzie dzień, że i Tobie instrukcja ta bardzo się przyda i ułatwi napisanie poważnego programu.

Instrukcja skoku goto - wprowadzenie

Instrukcja skoku goto umożliwia przejście do dowolnego miejsca w programie. Pisząc do dowolnego miejsca w programie mam na myśli takie programy, jakie przedstawiłem Ci do tej pory.

Kiedy poznasz już czym są funkcje, wtedy dowiesz się, że za pomocą instrukcji goto można przejść do dowolnego miejsca, ale w bieżącym zasięgu (na razie tego nie musisz rozumieć).

Jednocześnie chcę bardzo wyraźnie zaznaczyć, że instrukcja goto nie jest w żaden sposób powiązana z pętlami! Co prawda najczęściej instrukcję skoku goto stosuje się często właśnie w przypadku pętli, jednak nie jest to żadnym wymogiem (w przeciwieństwie do instrukcji continue i instrukcji break, z tym, że tą drugą można również zastosować w przypadku instrukcji switch).

Instrukcja skoku goto - etykiety

Instrukcja skoku goto jest ściśle powiązana z etykietami. Ponieważ za pomocą instrukcji goto chcemy przejść do jakiegoś konkretnego miejsca w naszym programie, to musimy to miejsce jakoś oznaczyć. Do tego właśnie służą etykiety.

Sposób deklaracji etykiety jest następujący:

nazwaEtykiety:

Jak więc widzisz to nic trudnego. Zwróć tylko uwagę, że na końcu znajduje się dwukropek, a nie średnik - łatwo można się pomylić.

Nazwy etykiet muszą być różne między sobą, nie muszą jednak mieć innych nazw niż zmienne. Kompilator potrafi po prostu odróżnić nazwy etykiet i nazwy zmiennych.

Jeśli w programie mamy już etykietę, to żeby do niej przejść piszemy goto nazwaEtykiety; czyli na przykład goto koniec; Przy okazji warto dodać, że etykieta może znajdować się zarówno przed jak i po użyciu instrukcji goto.

Jeśli na przykład chcemy za pomocą instrukcji goto przejść np. do 22 linijki w naszym programie, to powinniśmy w linijce 21 napisać etykietę np. linia22:, a następnie w wybranym miejscu należy napisać goto linia22;

Oto prosty przykład programu z etykietami i instrukcją goto:

#include <iostream>

using namespace std;

int main()
{
poczatek:
  int i=5;
  ++i;
  goto koniec;
  i=0;
  cout <<"To jest jakis tekst. I tak nie zostanie wypisany"<<endl;

koniec:    
 cout <<"Zmienna i ma wartosc "<<i<<endl;  

 cout <<endl<<"Nacisnij ENTER aby zakonczyc..."<<endl;
 getchar();
 return 0;  
}
program nr 18.4

Jeśli dobrze się przyjrzysz to zauważysz, że w programie mamy dwie etykiety: poczatek i koniec. Tak naprawdę etykieta poczatek nie jest w ogóle potrzebna, bowiem nigdzie jej nie używamy. Mimo to została zapisana, aby udowodnić Ci, że sama etykieta nie zmienia nic w wykonaniu programu - zmiany powoduje dopiero instrukcja goto.

Gdybyśmy w powyższym programie nie zastosowali instrukcji goto, to wówczas końcowa wartość zmiennej i wyniosłaby 0. Ponieważ jednak używamy instrukcji skoku, omijając w ten sposób 2 linijki, to okazuje się, że wartość zmiennej i wynosi 6.

Instrukcja skoku goto - nie używaj!

Już teraz pragnę zwrócić Twoją uwagę na jedną kwestię: instrukcja skoku goto jest w zasadzie jedyną znaną mi instrukcją, która co prawda została wprowadzona do języka, jednak jej używanie jest uważane za wyjątkowo nieeleganckie.

Tak naprawdę program, w którym instrukcja goto została użyta, jest często uznawany przez bardziej zaawansowanych lub zawodowych programistów za pisany przez zupełnie początkującego. Dlatego jeśli tylko to możliwe, wystrzegaj się używania tej instrukcji.

W rzeczywistości instrukcję skoku goto wypada jedynie użyć w przypadku zagnieżdżonych pętli, jeśli chcemy przerwać wszystkie pętle (lub kilka z nich), bowiem jak wiesz za pomocą instrukcji break nie da się tego osiągnąć (chyba, że użyjesz sposobu pokazanego przeze mnie w poprzedniej lekcji).

Nie należy natomiast w żadnym przypadku stosować tej instrukcji do "zwykłych" zastosowań, bowiem zrozumienie programów napisanych z użyciem instrukcji skoku goto jest bardzo trudne, a czasami wręcz niemożliwe.

Właśnie takim przykładem nadużycia instrukcji skoku jest przedstawiony przed momentem program - w rzeczywistym świecie programistów, taki program nie ma się prawa pojawić, no chyba, że jest to rzeczywiście uzasadnione (a w 99% nie jest).

Przykłady użycia

Poniżej przedstawiam jeszcze 2 przykłady użycia instrukcji skoku goto - mam nadzieję, że dzięki nim uda Ci się przekonać, że naprawdę instrukcji tej należy używać tak rzadko jak tylko to możliwe.

#include <iostream>

using namespace std;

int main()
{
  for (unsigned i=0;i<3;++i)
  {
     cout <<"zewnetrzna dla i="<<i<<endl;
     for (unsigned int j=0;j<2;++j)
     {
        if (i==1 && j==1)
           goto koniec;
        cout <<"wewnetrzna dla j="<<j<<endl;
     }
  }
koniec:
  cout <<"Tekst kontrolny. Wyszlismy z zagniezdzonej petli"<<endl;
  // tutaj oczywiscie moze byc dalsza czesc programu

  cout <<endl<<"Nacisnij ENTER aby zakonczyc..."<<endl;
  getchar();
  return 0;  
}
program nr 18.5

Jeśli się zastanowisz, to przypomnisz sobie, że jest to ten sam przykład, który pojawił się w przypadku instrukcji break. Tutaj problem jest po prostu zrealizowany w prostszy sposób.

Użycie instrukcji skoku goto w takim przypadku na pewno nie będzie dla Ciebie powodem do wstydu. Nawet bardzo duże firmy programistyczne, tworzące kompilatory lub inne oprogramowanie, właśnie w przypadku zagnieżdżonych pętli używają właśnie instrukcji goto.

Teraz natomiast czas na bardzo zły przykład. Przyjrzyj się kodowi programu - nie uruchamiaj go ani też nie czytaj komentarza pod programem i postaraj się zgadnąć, co on robi. Dopiero wtedy uruchom program i czytaj dalej artykuł. Oto wspomniany program:

#include <iostream>

using namespace std;

int main()
{
  int i=0;
poczatek:
  cout <<"Zmienna i ma wartosc "<<i<<endl;
  ++i;
  if (i<10)
     goto poczatek;

  cout <<endl<<"Nacisnij ENTER aby zakonczyc..."<<endl;
  getchar();
  return 0;  
}
program nr 18.6

Po uruchomieniu programu na pewno już wiesz, że powyższy przykład spełnia taką samą rolę jak pętla, w której zmienna sterująca przyjmuje wartości 1, 2, ..,8, 9. Doskonale wiesz, że taki przykład można zrealizować za pomocą pętli for, while oraz do while.

Po co więc stosować instrukcję goto? No właśnie. Nie należy tego w takim przypadku robić! Nawet najprostszy program można strasznie skomplikować, stosując etykiety i instrukcję skoku goto.

Dodatkowo sprawdź co by się stało, gdyby dwie linijki: int i=0; i poczatek: zostały zamienione - program się zapętli, bowiem za każdym razem jest tworzona nowa zmienna o wartości 0. Jak więc widzisz - taki prosty program, a używając instrukcji goto można doprowadzić do katastrofy.

Radzę Ci zatem stosować instrukcję skoku goto tylko wtedy, gdy jest to naprawdę uzasadnione, czyli w rzeczywistości tylko w przypadku przerywania działania zagnieżdżonych pętli.

W tej lekcji poznaleś dwie instrukcje: continue oraz goto - obie używane w pracy z pętlami i obie ułatwiające posługiwanie się nimi. Na pewno warto znać obie te instrukcje, jednak jak już podkreślałem ich znajomość nie jest absolutnie konieczna do dalszej nauki języka C++.

powrót