Rozdział 6. Instrukcje sterujące

13 lipca 2012
1 gwiadka2 gwiazdki3 gwiazdki4 gwiazdki5 gwiazdek

Program musi umieć podejmować decyzje. Aby mu to umożliwić, programiści używają tzw. instrukcji sterujących. Pojęcie to obejmuje dwa rodzaje konstrukcji programistycznych:

  • Instrukcje warunkowe: pozwalają na pisanie poleceń typu „jeśli ten warunek zostanie spełniony, zrób to i tamto”.
  • Pętle: pozwalają wielokrotnie wykonać wybrany zestaw instrukcji.

Umiejętność posługiwania się instrukcjami sterującymi jest dla programisty fundamentalna! Dlatego mimo że treść tego rozdziału nie jest trudna do zrozumienia, przeczytaj go wyjątkowo uważnie, aby dobrze zrozumieć wszystkie opisywane w nim zagadnienia. Instrukcji sterujących będziesz używać we wszystkich programach, a co ciekawsze są one obecne także we wszystkich innych językach. Mogą się co najwyżej nieco różnić składnią, ale ogólna zasada ich działania jest zawsze taka sama.

Konstrukcje opisywane w tym rozdziale są takie same w języku C i C++, a więc jeśli znasz już język C, to nie znajdziesz tu nic nowego. A jeśli nigdy nie miałeś do czynienia z językiem C, to też nie ma problemu, ponieważ wszystko Ci dokładnie wytłumaczę.

6.1. Instrukcje warunkowe

Żeby program samodzielnie podejmował decyzje, w jego kodzie źródłowym muszą znajdować się instrukcje warunkowe. Ogólna zasada jest prosta: chodzi o to, aby sprawić, żeby program reagował na różne sposoby w zależności od sytuacji. W tej części rozdziału znajdziesz objaśnienie działania konstrukcji programistycznych, które pozwalają utworzyć tego rodzaju mechanizmy.

Najpierw jednak musisz poznać operatory służące do porównywania zawartości zmiennych. Będą one potrzebne do pisania instrukcji programistycznych w rodzaju „Czy wartość tej zmiennej jest większa od 10?”, „Czy ta zmienna zawiera hasło?”.

Poniżej znajduje się tabela symboli porównawczych, których trzeba nauczyć się na pamięć:

SymbolOpis
==Równy
>Większy
<Mniejszy
>=Większy lub równy
<=Mniejszy lub równy
!=Różny
Symbol równości składa się z dwóch znaków =. Początkujący programiści często zapominają o tym i wpisują tylko jeden znak, który w języku C++ ma całkiem inne znaczenie.

W języku C++ dostępnych jest więcej instrukcji warunkowych, niż wymieniłem, ale bez wątpienia najważniejszą z nich wszystkich jest instrukcja if. Trudno wyobrazić sobie programowanie bez niej.

6.1.1. Instrukcja warunkowa if

Wcześniej napisałem, że instrukcje warunkowe służą do porównywania zawartości zmiennych. Aby pomóc Ci dobrze zrozumieć na czym to polega, napiszemy prosty program wykonujący testy porównawcze.

Zaczniemy od poniższego kodu:

#include <iostream>
using namespace std;
int main()
{
    int lbDzieci(2);

    return 0;
}

Jak na razie program ten nic nie robi. Zawiera tylko deklarację zmiennej o nazwie lbDzieci (liczba dzieci), po której następuje jego zamknięcie.

Pierwszy warunek if

Teraz dodamy do naszego programu instrukcję warunkową, która będzie powodowała wyświetlenie gratulacji, jeśli użytkownik ma dzieci. Instrukcja ta będzie sprawdzać, czy zmienna lbDzieci ma wartość większą od zera i jeśli tak, będzie wyświetlać odpowiedni napis.

#include <iostream>
using namespace std;
int main()
{
	int lbDzieci(2);
	if (lbDzieci > 0)
	{
		cout << "Masz dzieci. Brawo!" << endl;
	}
	cout << "Koniec programu" << endl;
	return 0;
}

Wynik działania tego kodu jest następujący:

Masz dzieci. Brawo! Koniec programu.

Przyjrzyjmy się wierszowi kodu if (lbDzieci > 0).

Po przetłumaczeniu na język ludzki brzmiałby on następująco: „Jeśli liczba dzieci jest większa od 0” (if po angielsku oznacza „jeśli”). Jeżeli wynik testu jest pozytywny (tzn. osoba ma dzieci), komputer odczytuje i wykonuje kod znajdujący się w klamrach, a więc wyświetla napis Masz dzieci. Brawo!

A co się dzieje, jeśli osoba nie ma dzieci? Gdy wynik testu jest negatywny, komputer pomija instrukcje znajdujące się w klamrach i kontynuuje wykonywanie programu od pierwszej instrukcji znajdującej się za klamrą zamykającą. W przypadku naszego programu, jeśli osoba nie ma dzieci, zostanie tylko wyświetlony napis: Koniec programu.

Wypróbuj to! Zmień wartość zmiennej lbDzieci na 0 i zobacz, co się stanie.

Instrukcja else, czyli co robić, gdy test wypadnie negatywnie

Chciałbyś, aby Twój program robił coś konkretnego także wówczas, gdy warunek nie zostanie spełniony? Rzeczywiście na razie, jeśli osoba nie ma dzieci, nasz program nic z tą informacją nie robi.

Na szczęście istnieje słowo kluczowe else, które oznacza „w przeciwnym przypadku”. Za jego pomocą możemy sprawić, aby jeśli użytkownik nie ma dzieci wyświetlany był inny napis:

#include <iostream>
using namespace std;
int main()
{
	int lbDzieci(0);
	if (lbDzieci > 0)
	{
		cout << "Masz dzieci. Brawo!" << endl;
	}
	else
	{
		cout << "Ojej, nie masz dzieci?" <<
			endl;
	}
	cout << "Koniec programu" << endl;
	return 0;
}

Wynik działania tego programu będzie następujący:

Ojej, nie masz dzieci? Koniec programu

Zwróć uwagę, że zmieniłem wartość zmiennej lbDzieci na 0. Jeśli znów zmienisz ją na wartość powyżej zera, zostanie wyświetlony poprzedni napis.

Jak dokładnie to działa? To bardzo proste: komputer najpierw sprawdza warunek if, aby dowiedzieć się czy jest on spełniony, czy nie. W tym przypadku warunek nie jest spełniony, a więc następuje przejście do instrukcji znajdujących się między klamrami pod instrukcją else i wykonanie ich. W ten sposób sprawiliśmy, że komputer wykonał instrukcje bloku else.

Schemat instrukcji warunkowej

Rysunek 6.1. Schemat instrukcji warunkowej

Instrukcja else if: dodawanie kolejnych warunków

Można też utworzyć zestaw kilku instrukcji warunkowych. Wyobraź sobie następującą sytuację:

  • Jeśli liczba dzieci jest równa 0, wyświetl napis „…”.
  • Jeśli liczba dzieci jest równa 1, wyświetl napis „…”.
  • Jeśli liczba dzieci jest równa 2, wyświetl napis „…”.
  • W pozostałych przypadkach wyświetl „…”.

Aby utworzyć taki zestaw warunków, musimy użyć instrukcji else if (w przeciwnym razie, jeśli). Testy będą wykonywane po kolei, aż zostanie znaleziony taki, który zakończy się pozytywnie.

#include <iostream>
using namespace std;
int main()
{
	int lbDzieci(2);
	if (lbDzieci == 0)
	{
		cout << "Ojej, nie masz dzieci?" <<
			endl;
	}
	else if (lbDzieci == 1)
	{
		cout << "No to kiedy drugie?" << endl;
	}
	else if (lbDzieci == 2)
	{
		cout << "Ale masz piękne dzieci!" << endl;
	}
	else
	{
		cout << "Dobrze, może już wystarczy!" << endl;
	}
	cout << "Koniec programu" << endl;
	return 0;
}

Skomplikowane? Na pewno nie.

My mamy dwoje dzieci:

  • Komputer sprawdza czy mamy 0 dzieci.
  • Wynik jest negatywny, a więc komputer przechodzi do pierwszej instrukcji else if, aby sprawdzić czy mamy 1 dziecko. Znów pudło!
  • Komputer przechodzi zatem do kolejnej instrukcji else if, aby sprawdzić czy mamy dwoje dzieci. Wynik testu jest pozytywny, a więc zostaje wyświetlony napis Ale masz piękne dzieci!

Jeśli żaden z warunków nie zostanie spełniony, komputer wykona zawartość instrukcji else, czyli wyświetli napis Dobrze, może już wystarczy!

6.1.2. Instrukcja warunkowa switch

Teoretycznie instrukcja if pozwala na zrealizowanie wszystkich testów warunkowych, jakich można potrzebować. Istnieją jednak także inne instrukcje warunkowe, np. switch, pozwalające w prostszy sposób wyrazić np. testy wielu wartości dla jednej zmiennej.

Weźmy na przykład nasz poprzedni test dotyczący liczby dzieci:

Czy ma 0 dzieci?
Czy ma 1 dziecko?
Czy ma 2 dzieci?

Testy tego typu można wyrazić za pomocą serii instrukcji if.. else if... else, ale lepiej do tego celu użyć instrukcji switch, której struktura w takich przypadkach jest bardziej przejrzysta. Poniżej znajduje się poprzedni program z instrukcją if zamienioną na switch:

#include <iostream>
using namespace std;
int main()
{
	int lbDzieci(2);
	switch (lbDzieci)
	{
	case 0:
		cout << "Ojej, nie masz dzieci?" <<
			endl;
		break;
	case 1:
		cout << "No to kiedy drugie?" << endl;
		break;
	case 2:
		cout << "Ale masz piękne dzieci!" << endl;
		break;
	default:
		cout << "Dobrze, może już wystarczy!" << endl;
			break;
	}
	return 0;
}

Wynik działania tego programu będzie następujący:

Ale masz piękne dzieci

Struktura tej konstrukcji warunkowej jest nieco inna. Najpierw podajemy informację, że będziemy badać zmienną o nazwie lbDzieci (wiersz 9). Następnie sprawdzamy wszystkie możliwe przypadki: czy wskazana zmienna ma wartość 0, 1, 2 itd.

Instrukcja break w każdym przypadku sprawia, że po wykonaniu jego kodu komputer przerywa wykonywanie dalszych testów. Jej używanie nie jest obowiązkowe, jeśli nie chce się przerywać testów, ale ja zawsze ją stosuję.

Znajdująca się na końcu instrukcja default jest odpowiednikiem instrukcji else. Jej kod zostaje wykonany wtedy, gdy żaden z testów nie zakończy się pozytywnie.

W instrukcji switch można testować tylko równość, tzn. nie można pisać testów typu „Jeśli liczba dzieci jest większa od 2”. Jeżeli potrzebujesz tego rodzaju warunku, użyj instrukcji if.

Ponadto w warunku instrukcji switch można porównywać tylko liczby całkowite (int, unsigned int, char). Nie można zatem porównywać np. wartości typu double.

6.2. Wartości logiczne i łączenie warunków

Trochę poszerzymy naszą wiedzę na temat pisania warunków. W tym podrozdziale poznasz bardziej zaawansowane i bardzo ważne techniki pisania instrukcji warunkowych. Nauczysz się używać wartości i operatorów logicznych.

6.2.1. Wartości logiczne

Pamiętasz typ bool? Zmienna tego typu może zawierać tylko jedną z dwóch wartości: true (prawda) lub false (fałsz).

Ten typ wartości jest często używany w instrukcjach warunkowych. Jakby się nad tym zastanowić, to jest to nawet logiczne: warunek może być spełniony (prawda) albo niespełniony (fałsz). Do wyrażenia tego doskonale nadają się właśnie zmienne logiczne.

Przypominam zmienne logiczne właśnie teraz, ponieważ zmiennych tego typu w instrukcjach warunkowych używa się w szczególny sposób. Spójrz na poniższy kod:

bool dorosly(true);

if (dorosly == true)
{
    cout << "Jesteś dorosły!" << endl;
}

Ta instrukcja warunkowa sprawdza zmienną dorosly i jeśli ma ona wartość true, wyświetla odpowiedni napis.

Zapis ten można skrócić, tzn. możemy opuścić część warunku == true. Spójrz na poniższy kod, który działa identycznie, jak poprzedni:

bool dorosly(true);

if (dorosly)
{
    cout << "Jesteś dorosły!" << endl;
}

Komputer sam „rozumie”, że chcemy sprawdzić czy zmienna logiczna ma wartość true i dlatego nie musimy mu tego „mówić” za pomocą kodu == true.

Czy to nie utrudnia czytania kodu? Nie, wręcz przeciwnie, dzięki temu kod jest krótszy i łatwiejszy do zrozumienia. Instrukcję if (dorosly) można czytać „Jeśli jest dorosły”.

Warto stosować ten skrócony sposób zapisu, ponieważ dzięki temu kod jest bardziej przejrzysty i łatwiejszy do czytania.

6.2.2. Łączenie warunków

Jeśli warunek jest złożony, to można go wyrazić w jednej instrukcji if przy użyciu specjalnych operatorów logicznych: && (i), || (lub) oraz ! (nie).

Operator logiczny && (logiczne i, iloczyn logiczny)

Wyobraź sobie, że chcemy sprawdzić czy dana osoba jest dorosła i ma przynajmniej jedno dziecko. Test ten możemy w kodzie wyrazić następująco:

if (dorosly && lbDzieci >= 1)

Dwa znaki & obok siebie stanowią operator iloczynu logicznego, czyli logicznego i. Powyższy warunek po polsku moglibyśmy przeczytać następująco: „Jeśli osoba jest dorosła i liczba dzieci jest równa 1 lub większa”.

Warunek ten można by było także zapisać w ten sposób: if (dorosly == true && lbDzieci >= 1). Jak już wyjaśniałem cząstkę == true w testach wartości logicznych można opuścić i warto to robić, aby zwiększyć czytelność kodu źródłowego.

Operator logiczny || (suma logiczna, logiczne lub)

Sumę logiczną (zwaną też logicznym lub) oznaczamy za pomocą dwóch pionowych kresek — ||. Na klawiaturze QWERTY pionowa kreska znajduje się nad klawiszem Enter i wpisuje się ją z klawiszem Shift (bez niego wpiszemy ).

Przy użyciu tego operatora można na przykład sprawdzić czy liczba dzieci jest równa 1 LUB 2:

if (lbDzieci == 1 || lbDzieci == 2)

Operator negacji logicznej

Ostatni symbol, o którym chcę Ci jeszcze teraz napisać to znak ! oznaczający negację logiczną. W programowaniu znak ! zazwyczaj oznacza „nie”. Stawia się go przed warunkiem i jego znaczenie jest następujące „Jeśli to nie prawda”:

if (!dorosly)

Powyższy kod należy przeczytać: „Jeśli nie jest dorosły”.

6.3. Pętle

Pętle pozwalają wielokrotnie wykonywać wybrane bloki kodu źródłowego. Ich ogólna zasada działania jest następująca:

Schemat działania pętli

Rysunek 6.2. Schemat działania pętli

  1. Komputer odczytuje instrukcje od góry do dołu.
  2. Gdy wykona ostatnią instrukcję pętli, wraca do pierwszej.
  3. Rozpoczyna odczytywanie wszystkich instrukcji od początku.
  4. Po wykonaniu ostatniej instrukcji, program wraca do pierwszej itd.

Instrukcje pętli są wykonywane dopóki warunek pętli jest spełniony. Można na przykład napisać następującą pętlę: „Dopóki użytkownik będzie wpisywał liczbę dzieci mniejszą od zera, wyświetlaj prośbę o podanie liczby dzieci”.

Istnieją trzy typ pętli: while, do ... while oraz for.

6.3.1. Pętla while

Ogólna budowa pętli while jest następująca:

while (warunek)
{
    /* Instrukcje do wielokrotnego wykonania */
}

Wszystko, co znajduje się między klamrami będzie wykonywane dotąd, aż warunek zostanie spełniony.

Działanie tej pętli w praktyce prześledzimy na przykładzie programu sprawdzającego podaną przez użytkownika liczbę dzieci i wyświetlającego prośbę o podanie tej liczby dopóki nie zostanie wpisana liczba większa od zera. W ten sposób można zabezpieczyć program przed niepoprawnymi danymi wejściowymi.

int main()
{
	int lbDzieci(-1); // Ujemna liczba, dzięki której pętla zostanie wykonana
		while (lbDzieci < 0)
		{
			cout << "Ile masz dzieci?" << endl;
			cin >> lbDzieci;
		}
		cout << "Dziękuję za podanie poprawnej liczby. Masz dzieci: " << lbDzieci << endl;
		return 0;
}

Oto przykładowy wynik działania tego programu:

Ile masz dzieci? -3 Ile masz dzieci? -5 Ile masz dzieci? 2 Dziękuję za podanie poprawnej liczby. Masz dzieci: 2

Dopóki użytkownik będzie wpisywał ujemną liczbę, pętla będzie powtarzana. Test while (lbDzieci < 0) oznacza dla komputera „Dopóki liczba dzieci jest mniejsza od 0”. Gdy użytkownik wpisze liczbę równą 0 lub większą, pętla zakończy się i program przejdzie do wykonywania instrukcji znajdujących się za zamknięciem klamry.

Zwróć uwagę, że zmienna lbDzieci została zainicjowana wartością -1. Jest to potrzebne, aby komputer w ogóle rozpoczął wykonywanie pętli.

6.3.2. Pętla do … while

Ta pętla jest bardzo podobna do poprzedniej. Różni się od pętli while tylko tym, że jej warunek zostaje sprawdzony na końcu, a nie na początku. Dzięki temu instrukcje pętli na pewno zostaną wykonane przynajmniej raz.

Ogólna budowa tej pętli jest następująca:

do
{
    /* Instrukcje */
} while (warunek);

Zwróć uwagę, że na końcu znajduje się średnik!

Przepiszemy poprzedni przykładowy program przy użyciu pętli do ... while:

int main()
{
	int lbDzieci(0);
	do
	{
		cout << "Ile masz dzieci?" << endl;
		cin >> lbDzieci;
	} while (lbDzieci < 0);
	cout << "Dziękuję za podanie poprawnej liczby dzieci. Masz dzieci: " << lbDzieci << endl;
		return 0;
}

Zasada jest ta sama i ten program działa tak samo, jak poprzedni. Najważniejszą cechą pętli do ... while jest to, że jej instrukcje zawsze są wykonywane przynajmniej raz.

Dzięki temu nie trzeba inicjować zmiennej lbDzieci wartością -1, co jest niewątpliwą zaletą tego rozwiązania. Tym razem zmienna ta została zainicjowana zerem (jak to się zazwyczaj robi), a ponieważ warunek jest sprawdzany dopiero po pierwszym wykonaniu zawartości pętli, instrukcje przynajmniej raz zostaną wykonane.

6.3.3. Pętla for

Ta bardzo często używana pętla łączy w sobie trzy elementy: inicjację, warunek oraz inkrementację. Jej ogólna forma jest następująca:

for (inicjacja; warunek; inkrementacja)
{
}

Poniżej znajduje się przykład jej zastosowania do wyświetlenia liczb od 0 do 9:

int main()
{
    int obliczenia(0);

    for (obliczenia = 0 ; obliczenia < 10 ; obliczenia++)
    {
        cout << obliczenia << endl;
    }

    return 0;
}

Wynik działania tego kodu jest następujący:

0 1 2 3 4 5 6 7 8 9

Część sterująca pętli for składa się z trzech instrukcji, które zostały wymienione wcześniej. Poniżej znajduje się ich szczegółowy opis:

  • Inicjacja (obliczenia = 0): zmienna obliczenia zostaje na samym początku pętli ustawiona na zero. W tym przypadku inicjacja ta została też dokonana w wierszu powyżej, a więc tak naprawdę nie jest ona tu potrzebna.
  • Warunek (obliczenia < 10): w każdym cyklu pętli sprawdzamy, czy zmienna obliczenia ma wartość mniejszą od 10.
  • Inkrementacja (obliczenia++): po zakończeniu każdego cyklu pętli zwiększamy zmienną obliczenia o 1. To właśnie dlatego na ekranie pojawiają się kolejne cyfry od 0 do 9.
Inkrementację można zastąpić inną operacją. Ostatni element pętli for służy do modyfikowania zmiennej w dowolny sposób, a więc można np. zastosować dekrementację (obliczenia–) albo zwiększanie o 2 (obliczenia += 2) itp.

Należy zaznaczyć, że zmienną można zdefiniować bezpośrednio w pętli, co pozwala skrócić kod źródłowy:

int main()
{
    for (int obliczenia(0) ; obliczenia < 10 ; obliczenia++)
    {
    cout << obliczenia << endl;
    }

    return 0;
}

W takim przypadku zmienna istnieje tylko w czasie wykonywania pętli. Tej krótkiej formy zapisu pętli for używa się wówczas, gdy zmienna pętlowa nie jest potrzebna nigdzie indziej w programie, a więc w większości przypadków.

Kiedy używać pętli for a kiedy while?

Pętli for używa się wtedy, gdy liczba powtórzeń (iteracji) jest znana. Natomiast pętli while używa się najczęściej wtedy, gdy liczba iteracji nie jest znana z góry.

Znasz już wszystkie najważniejsze instrukcje sterujące języka C++. Są to te elementy języka, bez których programowanie nie miałoby sensu.

Poświęć trochę czasu na dokładne przyswojenie sobie tych wiadomości, ponieważ instrukcji warunkowych i sterujących będziemy używać praktycznie w każdym programie do końca kursu.

Autor: Mathieu Nebra i Matthieu Schaller

Źródło: http://openclassrooms.com/courses/programmez-avec-le-langage-c/les-structures-de-controle-4

Tłumaczenie: Łukasz Piwko

Treść tej strony dostępna jest na zasadach licencji CC BY-NC-SA 2.0

9 komentarzy

  1. witam
    niestety napisany przez pana program zawiera mnostwo bledow oraz jest slabo zoptymalizowany pod kątem procesorow intela. pragne pana poinformowac, iz takie bzdury sa zupelnym zaprzeczeniem ifnormatyki, czlowieku idz sie poucz najpierw, otworz jakies ksiazki czy cos, potem bierz sie za pisanie blogow na temat C++. masz ty w ogole rozum i godnosc czlowieka?

    Odpowiedz
    • Zastanawiam się, czy ten komentarz na pewno dotyczy tego właśnie artykułu? W każdym razie przekażę uwagi autorom, aby poczytali coś o procesorach Intela…

      Odpowiedz
  2. nie ten co trzeba wkleiłem, ten wczesniejszy z samym while

    Odpowiedz
  3. int main()
    {
    int lbDzieci(0);
    do
    {
    cout << "Ile masz dzieci?" <> lbDzieci;
    } while (lbDzieci < 0);
    cout << "Dziękuję za podanie poprawnej liczby dzieci. Masz dzieci: " << lbDzieci << endl;
    return 0;
    }

    ten kod podany przez Ciebie nie dziala, niezadeklarowane cout i cin

    Odpowiedz
    • a nie sorry działa, moj glupi blad

      Odpowiedz
  4. te biblioteki to string fstream i iostream

    Odpowiedz
  5. #include
    #include
    #include
    using namespace std;;
    int main()
    {
    string qw=”c:/users/kamil/desktop/plik.bat”;
    ofstream as(„c:/users/kamil/desktop/plik.bat”,ios::app);
    int q;
    cout<>q;
    string w [q];
    for (int u=0;u<q;u++)
    {
    cout <<"podaj kod"<<endl;
    if (u==q-1)
    {
    getline(cin, w[u]);
    as << w[u];
    as << " ";

    as << " ";
    }
    else
    {

    getline(cin.ignore(), w[u]);
    as << w[u];
    as << " ";
    as <<"&";
    as << " ";
    }

    }

    return 0;;
    }
    nie chce działać prawidłowo prosze o pomoc i w razie potrzeby wyrozumiałość

    Odpowiedz
  6. te biblioteki które sie nie wkleiły to iostream i string
    oczywiscie w dev c++

    Odpowiedz
  7. #include
    #include
    using namespace std;
    int main()
    {

    int w = 2;
    int r = 1;
    while ( w == 2)
    {

    const string k = „pass”;
    cout <>b;

    if (k == b)
    {
    w = r;
    cout << "witaj";
    cin.get();

    }
    else
    {
    cout<<"blad";
    cin.get();

    }
    cin.get();
    }
    }
    wyszło mi jak troche pogrzebałem

    Odpowiedz

Dyskusja

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *