Podręcznik

Strona: SEZAM - System Edukacyjnych Zasobów Akademickich i Multimedialnych
Kurs: 3. Wyrażenia i pętle
Książka: Podręcznik
Wydrukowane przez użytkownika: Gość
Data: poniedziałek, 7 lipca 2025, 08:14

1. Wyrażenia, operatory i funkcje matematyczne

Wyrażenia są formułami, które zawsze dają jakiś wynik, w zasadzie wszystko co staje się wartością, jest w C++ uważane za wyrażenie. Do budowy wyrażeń wykorzystuje się następujące elementy języka:

  1. stałe (np. liczby),
  2. zmienne,
  3. nawiasy okrągłe,
  4. operatory arytmetyczne,
  5. operatory relacji,
  6. operatory logiczne,
  7. funkcje, takie jak np. sin(x).

Tak więc stałe i zmienne są w C++ wyrażeniami i jeśli gdzieś dalej powiemy, że w danej konstrukcji występuje wyrażenie, to trzeba pamiętać, że może ono być na przykład liczbą, napisem czy nazwą zmiennej.

W wyrażeniach operatory działają na operandach (argumentach): wiążą ze sobą stałe, zmienne i - całe wyrażenia ujęte w nawiasy okrągłe. Każdy operand w C++ jest wyrażeniem. W wyniku działania operatora na operandach obliczona zostaje wartość wyrażenia. Mówimy, że operator zwraca wartość obliczonego wyrażenia. 
W języku C++ występują następujące podstawowe typy operatorów:

  1. Operatory arytmetyczne. Są to między innymi dobrze Wam znane operatory dodawania, odejmowania, itp. Dostępne w C++ operatory arytmetyczne opiszemy w następnym podrozdziale.
  2. Operatory logiczne, czyli operatory koniunkcji, alternatywy, negacji itp.  Operatory logiczne zawsze zwracają wartość logiczną (prawdę lub fałsz).
  3. Operatory relacji, podobnie jak operatory logiczne, zwracają zawsze wartość logiczną.
  4. Operatory bitowe. Pozwalają one na wykonywanie operacji bezpośrednio na bitach, czyli najmniejszych niepodzielnych jednostkach pamięci. Każda liczba, tekst, obraz czy dźwięk są pamiętane jako zestawy bitów pogrupowane w bajty. Język C i wywodzące się z niego C++ są często wykorzystywane do bezpośredniego programowania sprzętu, mają więc dość silnie rozwinięte metody operacji na bitach i liczbach zapisanych dwójkowo. 
  5. Operator przecinka. Tak - to nie pomyłka. W C++ przecinek jest także operatorem, a nie tylko służy do oddzielania od siebie zmiennych tego samego typu czy też oddzielania argumentów funkcji. Przecinek może być także wykorzystany do oddzielania od siebie wyrażeń, co powoduje, że wszystkie zostaną obliczone, natomiast tylko ostatnie z nich zostanie wykorzystane jako wartość. I znów stwierdzimy, że jest to skomplikowany i charakterystyczny dla C++ operator, więc nie będziemy się nim dalej zajmowali.
  6. Operatory rzutowania, które stosuje się, by dokonać jawnej konwersji wartości. Operatorów rzutowania w C++ jest kilka, większość z nich nie będzie wykorzystywana w trakcie tego kursu. Najczęściej będziemy stosować konwersję typu.
Poszczególne operatory zostaną omówione w kolejnych podrozdziałach.

1.1. Operator podstawienia

Jedną z podstawowych operacji  w każdym języku programowania jest instrukcja podstawienia, a więc przypisania wartości. Instrukcja podstawienia w języku C++ to operacja, która przypisuje wartość jednej zmiennej do innej zmiennej lub wyrażenia. W języku C++, instrukcja podstawienia wykorzystuje operator przypisania =. Ogólna składnia instrukcji podstawienia jest następująca:

zmienna = wyrażenie;

Poniżej w kodzie znajdują się przykładowe operacje przypisania wartości dla różnego typu zmiennych:

zmienna_calkowita = 8;

zmienna_rzeczywista = 1.25;
druga_zmienna_rzeczywista = .25;

zmienna_znakowa = 'A';

napis = "Ala ma kota.";

prawda = true;
Oczywiście dana zmienna musi być wcześniej zdefiniowana np. int zmienna;
int liczba;
liczba = 6;

int suma, licznik;
licznik = 4;
suma = 5; 
Można zdefiniować zmienną oraz jednocześnie przypisać do niej wartość.
int liczba = 6;
double suma = 0, iloczyn = 1, dane;
Zmienne nie zainicjowane w trakcie definiowania pozostają nieokreślone (mają wartość przypadkową),  dopóki nie przypiszemy im określonej wartości - nie można liczyć na to, że mają na początku np. wartość zerową (a to bardzo częsty błąd początkujących).
Możliwa jest także inicjalizacja wartości zmiennych przy pomocy klamer {}. Inicjalizacja zmiennych przy pomocy klamer została wprowadzona do języka C++ wraz ze standardem C++11, który został formalnie zatwierdzony przez ISO (ang. International Organization for Standardization) w sierpniu 2011 roku. Standard C++11 przyniósł wiele nowych funkcji i udoskonaleń do języka, a jednym z nich była właśnie jednolita inicjalizacja (ang. uniform initialization) przy użyciu nawiasów klamrowych {}.

int licznik = {0}; // notacja ze znakiem przypisania =
double iloczyn {1}; // notacja bez znaku = (częściej stosowana)
double suma {}; // puste klamry oznaczają inicjalizację wartością zerową
Istnieje wiele korzyści wynikających z zastosowania klamer do inicjalizacji zmiennych. Możemy wśród nich wymienić:
  1. Zapobieganie niezamierzonym konwersjom: Inicjalizacja za pomocą klamer zapobiega niebezpiecznym konwersjom typów, które mogą prowadzić do utraty danych.
    int x = 3.14; // Dozwolone, x staje się 3 int y {3.14}; // Błąd kompilacji: konwersja z double na int może powodować utratę danych
  2. Jednoznaczna inicjalizacja: Inicjalizacja za pomocą klamer jednoznacznie określa, że zmienna powinna być zainicjalizowana, a nie przypisana.
  3. Ujednolicona składnia: Ta forma inicjalizacji zapewnia spójny sposób inicjalizacji zarówno typów podstawowych, jak i złożonych.
Wiemy już jak przypisywać wartości do zmiennych różnego typu. Kolejnym etap w nauce programowania są podstawowe operacje jakie możemy wykonywać na zmiennych. W języku C++ podstawowe operacje na zmiennych obejmują przypisywanie wartości, operacje arytmetyczne, logiczne, relacyjne, inkrementację i dekrementację, operacje bitowe oraz operacje na wskaźnikach. Każda z tych operacji ma swoje specyficzne zastosowanie i składnię. 

1.2. Operatory arytmetyczne

Możemy wyróżnić następujące operacje arytmetyczne na zmiennych w języku C++:
  • dodawanie (+),
  • odejmowanie (-),
  • mnożenie (*),
  • dzielenie (/),
  • reszta z dzielenia (%).
Poniżej przykładowe operacje arytmetyczne:
int x = 10;
int y = 3;

int sum = x + y; // Dodawanie: sum = 13
int difference = x - y; // Odejmowanie: difference = 7
int product = x * y; // Mnożenie: product = 30
int quotient = x / y; // Dzielenie: quotient = 3 (dzielenie całkowite)
int remainder = x % y; // Reszta z dzielenia: remainder = 1Przy omawianiu operacji podstawiania warto podać zasadę na jakiej wyznaczona jest wartość wyrażenia w podanych operacjach.


Na rysunku widzimy operację wyznaczania sumy dwóch zmiennych (1) oraz operację przypisania wartości do zmiennej. W języku C++ w [pierwszej kolejności zostanie wyznaczona wartość wyrażenia po prawej stronie (1), a dopiero w drugim kroku zostanie wykonana operacja przypisania wartości wyrażenia do zmiennej x (2). 

Pomiędzy zmienną a wyrażeniem w C++ nie musi istnieć zgodność typów!
Musicie uważać, bo jeśli np. wynikiem wyrażenia jest wartość rzeczywista, a zmienna jest typu całkowitego - kompilator sam zmieni wartość rzeczywistą na całkowitą bez informowania o tym programisty - po prostu obetnie (a nie zaokrągli) część po przecinku. Można to zaobserwować na prostym przykładzie.
int a = 1;
int b = 3;

double wynik = a/b;
W wyniku operacji dzielenia liczba całkowita a zostanie podzielona na wartość b. W wyniku działania dzielenia wartości 1 przez 3 wynik powinien być liczbą zmiennoprzecinkową. W języku C++ wynik operacji dzielenia zmiennych całkowitych (int) jest również liczbą całkowitą, nawet jeśli wynik matematyczny jest liczbą zmiennoprzecinkową. To zachowanie jest związane z regułami typu danych używanych w operacji. Kiedy obie operandy dzielenia są typu całkowitego, wynik również będzie typu całkowitego, a ewentualna część ułamkowa zostanie odrzucona (obcięta). W tym przypadku 1 / 3 daje 0 (część ułamkowa jest odrzucona). Aby uzyskać wynik dzielenia jako liczbę zmiennoprzecinkową, co spowoduje poprawne obliczenie 1 / 3 jako 0.33333, co najmniej jeden z operandów musi być typu zmiennoprzecinkowego (float, double, long double). 

Można to osiągnąć na kilka sposobów:
  1. Rzutowanie operandów za pomocą nawiasów (ang.C-style cast)
    double x = (double) a / b;
    
  2. Rzutowanie za pomocą static_cast
    double x = static_cast<double>(a) / b;
    // lub
    double x = a / static_cast<double>(b);
    
Rzutowanie za pomocą nawiasów jest prostą metodą przekształcania typów w C++, ale może być mniej czytelne i bezpieczne niż inne metody dostępne w C++. Niemniej jednak, jest to standardowy sposób rzutowania, który jest wciąż często używany w prostych przypadkach.
Rzutowanie przy pomocy static_cast jest preferowaną metodą rzutowania w C++, ponieważ jest bardziej bezpieczne i czytelne w porównaniu do rzutowania w stylu C. C-style cast jest bardziej ryzykowne, ponieważ pozwala na konwersję między praktycznie dowolnymi typami, co może prowadzić do błędów, które są trudne do wykrycia.

W trakcie implementacji aplikacji często istnieje konieczność zamiany miejscami dwóch zmiennych. Zamianę wartości pomiędzy dwiema zmiennymi można wykonać przy pomocy zmiennej pomocniczej.


W celu zamiany wartości pomiędzy zmiennymi nalezy wykonać następujące operacje:


    int a = 5;
    int b = 3;

    int schowek = b; // zapamiętanie wartości b w zmiennej schowek (1)
    b = a; // przypisanie wartości zmiennej a do zmiennej b (2)
    a = schowek; // przypisanie wartości zmiennej schowek do zmiennej a (3)

Po wykonaniu operacji wartości w zmiennych zostaną zamienione wzajemnie.

Na liście operatorów istnieje jeden operator który może być mniej znany. Jest to operacja zwracająca resztę z dzielenia tzw. dzielenie modulo (%). Przykładowo 5 % 2 jest równe 1, bo reszta z dzielenia 5 przez 2 jest równa 1, zaś -11  %  3 jest równe -2. Operator na zajęciach będzie głownie wykorzystywany w celu sprawdzenia do sprawdzania podzielności argumentów całkowitych.
Liczba całkowita x jest liczbą parzystą, jeśli spełnia warunek x % 2 == 0.
Dodatkowo, dla wygody programistów, wprowadzono dodatkowe operatory przypisania, które są połączeniem wybranej operacji arytmetycznej z klasycznym operatorem przypisania. Część z nich (te, które dotyczą operacji arytmetycznych) zamieściliśmy w tabeli poniżej.

Operator Przykład zastosowania Klasyczny zapis operacji
+= a += b a = a + b
-= a -= b a = a - b
*= a *= b a = a * b
/= a /= b a = a / b

1.3. Operatory inkrementacji i dekrementacji

Oprócz operatorów arytmetycznych w języku C++ dostępne są także operatory inkrementacji i dekrementacji. Pozwalają na wygodny zapis często spotykanych operacji zwiększenia i zmniejszenia wartości zmiennej o jeden. Tak więc zamiast napisać:
x = x + 1 ;
możemy napisać krócej
x++ ; (wersja przyrostkowa)
lub:
++x ; (wersja przedrostkowa)
i analogicznie zamiast:
x = x - 1 ;
możemy użyć operatora dekrementacji
x-- ; (wersja przyrostkowa)
lub:
--x ; (wersja przedrostkowa)

Operator przedrostkowy zwiększa (inkrementacja) lub zmniejsza (dekrementacja) wartość zmiennej, a następnie zwraca tę zmienioną wartość. Oznacza to, że zmienna zostaje zmodyfikowana przed użyciem jej w wyrażeniu.
int a = 5;
int b = ++a; // a zostaje zwiększone do 6, następnie b przyjmuje wartość 6
int c = --a; // a zostaje zmniejszone do 5, następnie c przyjmuje wartość 5

Operator przyrostkowy zwraca bieżącą wartość zmiennej, a następnie zwiększa (inkrementacja) lub zmniejsza (dekrementacja) wartość zmiennej. Oznacza to, że zmienna zostaje zmodyfikowana po użyciu jej w wyrażeniu.
int a = 5;
int b = a++; // b przyjmuje wartość 5, następnie a zostaje zwiększone do 6
int c = a--; // c przyjmuje wartość 6, następnie a zostaje zmniejszone do 5<br>

1.4. Operatory logiczne oraz relacji

W języku C++ możemy także na zmiennych wykonywać operacje logiczne.
bool a = true;
bool b = false;

bool resultAnd = a && b; // AND logiczny: resultAnd = false
bool resultOr = a || b; // OR logiczny: resultOr = true
bool resultNot = !a; // NOT logiczny: resultNot = false
Możemy także wykonywać operacje relacyjne.
int x = 10;
int y = 5;

bool isEqual = (x == y); // Równość: isEqual = false
bool isNotEqual = (x != y); // Nierówność: isNotEqual = true
bool isGreater = (x > y); // Większe: isGreater = true
bool isLess = (x < y); // Mniejsze: isLess = false
bool isGreaterOrEqual = (x >= y); // Większe lub równe: isGreaterOrEqual = true
bool isLessOrEqual = (x <= y); // Mniejsze lub równe: isLessOrEqual = falseCzasmi istnieje także konieczność wykonania operacji bitowych na poszczególnych zmiennych.
unsigned int a = 5; // binarnie: 0101
unsigned int b = 3; // binarnie: 0011

unsigned int resultAnd = a & b; // AND bitowy: resultAnd = 1 (binarnie: 0001)
unsigned int resultOr = a | b; // OR bitowy: resultOr = 7 (binarnie: 0111)
unsigned int resultXor = a ^ b; // XOR bitowy: resultXor = 6 (binarnie: 0110)
unsigned int resultNot = ~a; // NOT bitowy: resultNot = 4294967290 (binarnie: 11111111111111111111111111111010)
unsigned int resultShiftLeft = a << 1; // Przesunięcie w lewo: resultShiftLeft = 10 (binarnie: 1010)
unsigned int resultShiftRight = a >> 1; // Przesunięcie w prawo: resultShiftRight = 2 (binarnie: 0010)

1.5. Priorytety operatorów

W C++ priorytety operatorów określają kolejność, w jakiej operacje są wykonywane w wyrażeniu. Operatorzy o wyższym priorytecie są oceniani przed operatorami o niższym priorytecie. Jeśli operatorzy mają ten sam priorytet, ich kolejność jest określana przez ich łączność (lewo- lub prawostronną).

Priorytet Operator Opis Łączność
1 :: operator zakresu, przestrzeń nazw Lewostronna
2 a++ 
a--
()
[]
. ->
postinkrementacja 
postdekrementacja
wywołanie funkcji
indeksowanie
dostęp do członka klasy
Lewostronna
3 ++a
--a
~
!
+a
-a
&
*
new 
delete
sizeof
(typ)
preinkrementacja
predekrementacja
negacja bitowa
negacja logiczna
tożsamość
wartość przeciwna
operator adresu
operator wartości wskazywanej przez wskaźnik
operator tworzenia obiektu w pamięci
operator usuwania obiektu z pamięci
operator rozmiaru zmiennej, stałej, wyrażenia lub typu
operator rzutowania typów w stylu języka C
Prawostronna
4 .*
->*
operator dostępu do składowej
operator dostęp do składowej
Lewostronna
5 *
/
%
operator mnożenia
operator dzielenia
operator dzielenia modulo (reszta z dzielenia)
Lewostronna
6 +
-
operator dodawania
operator odejmowania
Lewostronna
7 <<
>>
przesunięcie bitowe w lewo
przesunięcie bitowe w prawo
Lewostronna
8 <=> operator porównania trójwartościowego Lewostronna
9 <
>
<=
>=
mniejsze
większe
mniejsze równe
większe równe
Lewostronna
10 ==
!=
równe
różne
Lewostronna
11 & operator bitowy AND Lewostronna
12 ^ operator bitowy XOR Lewostronna
13 | operator bitowy OR Lewostronna
14 && operator logiczny AND Lewostronna
15 || operator logiczny OR Lewostronna
16 a?b:c
=
+=
-=
*=
/=
%=
<<=
>>=
&=
^=
|=
operator przetwarzania warunkowego
operator przypisania
operator skróconego przypisania przez dodawanie
operator skróconego przypisania przez odejmowanie
operator skróconego przypisania przez mnożenie
operator skróconego przypisania przez dzielenie
operator skróconego przypisania przez dzielenie modulo
operator skróconego przypisania przez przesunięcie bitowe w lewo
operator skróconego przypisania przez przesunięcie bitowe w prawo
operator skróconego przypisania przez AND
operator skróconego przypisania przez XOR
operator skróconego przypisania przez OR 
Prawostronna
17 , operator przecinek Lewostronna
W jakim celu wprowadzono podane prorytety operatorów? W skrócie można powiedzieć, abyzapewnić określoną kolejność wykonywania operacji. W prostych operacjach jest to zazwyczaj mało skomplikowany problem. Ponieważ nawiasy posiadają prawie najwyższy priorytet wykonania, więc korzystając z nich możemy w łatwy sposób sterować kolejnością wykonywania poszczególnych działań. Proces decyzyjny w przypadku rozbudowanego wyrażenia najlepiej przedstawić na konkretnym przykładzie.
Mając następujące wyrażenie: 3 + 5 * 2 > (12 / 4) && (6 - 4 * 2) < 2 || 15 % 4 == 3 należy wyznaczyć jego wartość pokazując jednocześnie kolejność wykonywanych operacji.

W pierwszej kolejności wykonywane są operacje w nawiasach:



Następnie wykonywane jest mnożenie i dzielenie w głównym wyrażeniu:



Następnie wykonywane jest dodawanie w głównym wyrażeniu:



Następnie wykonywane są operatory porównania mniejszości oraz większości:



Kolejnym krokiem jest wykonanie operacji równości:



Następnie wykonywana jest operacja AND:



Ostatnie jest sprawdzenie OR:



W wyniku sprawdzenia całego wyrażenia oraz uwzględnieniu wszystkich priorytetów operatorów i kolejności wykonywania operacji otrzymaliśmy wartość true.

1.6. Funkcje matematyczne

W przypadku gdy nie wystarczają nam podstawowe operatory możemy skorzystać z dodatkowej biblioteki do obliczeń matematycznych. Biblioteka <cmath> w C++ jest standardową biblioteką. Zawiera szeroki zakres funkcji do wykonywania operacji matematycznych, podobnych do tych dostępnych w standardowej bibliotece C <math.h>. Poniżej znajdują się kluczowe funkcje i ich opis:
  1. Funkcje trygonometryczne:
    • sin(double x) - sinus z wartości x,
    • cos(double x) - cosinus z wartości x,
    • tan(double x) - tangens z wartości x,
    • asin(double x) - arcus sinus z wartości x,
    • acos(double x) - arcus cosinus z wartości x,
    • atan(double x) - arcus tangens z wartości x.
  2. Funkcje hiperboliczne:
    • sinh(double x) - sinus hiperboliczny z wartości x,
    • cosh(double x) - cosinus hiperboliczny z wartości x,
    • tanh(double x) - tangens hiperboliczny z wartości x.
  3. Funkcje wykładnicze i logarytmiczne:
    • exp(double x) - wartość e podniesiona do potęgi x,
    • log(double x) - logarytm naturalny z wartości x,
    • log10(double x) - logarytm o podstawie 10 z wartości x,
    • sqrt(double x) - pierwiastek kwadratowy z wartości x.
  4. Funkcje potęgowe:
    • pow(double base, double exp) - podniesienie wartości base do potęgi exp,
  5. Funkcje zaokrąglające:
    • ceil(double x) - wartość x jest zaokrąglana w górę do najbliższej liczby całkowitej nie mniejszej niż x,
    • floor(double x)- wartość x jest zaokrąglana w dół do najbliższej liczby całkowitej nie większej niż x,
    • round(double x) - wartość x jest zaokrąglana do najbliższej liczby całkowitej.
  6. Wartość bezwzględna:
    • fabs(double x) - wartość bezwzględna z liczby x.
  7. Funkcje minimum, maksimum i różnicy dodatniej:
    • fmin(double x, double y) - zwracana jest mniejsza z wartości x oraz y,
    • fmax(double x, double y) - zwracana jest większa z wartości x oraz y, 
    • fdim(double x, double y) - zwracana jest dodania różnica pomiędzy x i y.

2. Instrukcja wielokrotnego wyboru

W przypadku gdy istnieje konieczność wyboru z kilku możliwości możemy skorzystać z instrukcji warunkowej if. W przypadku kilku możliwych przypadków do wyboru jest to wystarczające. Jeżeli jednak chcemy dokonać wyboru z kilkudziesięciu przypadków wielokrotne wykorzystanie instrukcji warunkowej if może nie być zbyt wygodnym rozwiązaniem. Aby ułatwić zapis takiego i podobnych przypadków, kiedy wybieramy jedną opcję z wielu, powstała instrukcja wielokrotnego wyboru, tzw. instrukcja switch. Instrukcja switch jest kolejną, po if-ach, instrukcją strukturalną, jaką poznajecie. Ale jest to pojedyncza instrukcja i wszędzie tam, gdzie składnia języka dopuszcza jedną instrukcję, możemy ją wstawić. 

Składnia instrukcji switch:
switch (selektor) 

    case wart_11 : case wart_12 : ... : case wart_1i : Instrukcja_1; break;
    case wart_21 : ... : case wart_2j : Instrukcja_2; break;
    ...
    case wart_n1 : ... : case wart_nm : Instrukcja_n; break;
    default : Instrukcja_inna;
 }
Ogólnie - instrukcja switch składa się z dowolnej liczby przypadków. Każdy z nich jest definiowany przez jedną lub więcej wartości selektora. Każdą z wartości selektora wymieniamy osobno po słowie kluczowym case; mogą więc one tworzyć całą listę wartości, do której przypisana jest jedna wspólna instrukcja stojąca po ostatnim dwukropku.

Selektor może być wyrażeniem jednego z typów porządkowych (przeliczalnych), tzn. może być typu int, bool, char, ale nie może być typu rzeczywistego ani typu string.

Poniżej przedstawiono prostą symulację, która demonstruje działanie instrukcji wielokrotnego wyboru switch.

Wybierz wartość zmiennej n i zobacz, jak działa instrukcja switch w C++ z animacją:

switch (n) {
    case 1:
        cout << "Case 1";
        break;
    case 2:
        cout << "Case 2";
        break;
    case 3:
        cout << "Case 3";
        break;
    case 4:
        cout << "Case 4";
        break;
    default:
        cout << "Default case";
}
            

Na podsatwie symulacji możemy omówić działanie instrukcji switch. W zależności od wartości selektora, który jest umieszczony po instrukcji case zostaje wywołana dana grupa instrukcji występująca po dwukropku. Jeżeli wartość nie występuje po żadnym case to zostanie wykonany ciąg instrukcji, oddzielonych średnikami, znajdujący się po słowie default. Należy zaznaczyć, że w symulacji w każdym bloku instrukcji przypisanych do danej wartości selektora występuje instrukcja break. Instrukcja break powoduje przerwanie operacji switch oraz przejście do nawiasu zamykającego instrukcję wielokrotnego wyboru switch. Instrukcja break nie jest obligatoryjna. W przypadku gdy słowo break występuje, wykonana zostanie jedynie instrukcja przypisana do danej listy wartości. W przypadku gdy break nie będzie, wykonana zostanie instrukcja przypisana do danej wartości i wszystkie następne instrukcje, włącznie z tą przypisaną do default - aż do napotkania break lub końca instrukcji.

Każda z wartości selektora musi być jawnie wpisaną stałą (np. 5, true czy 'a') jednego typu lub wartością umożliwiającą jednoznaczne rzutowanie (np. zamiast znaku można podać jego kod w postaci liczby). Może być nawet wyrażeniem, ale takim, którego wartość można obliczyć na etapie kompilacji, czyli np. 2*2. Natomiast w żadnym razie nie mogą tutaj występować zmienne. Każda wartość (stała) może występować w wyrażeniu case tylko i wyłącznie raz.


3. Pętle while

W trakcie projektowania oraz implementacji aplikacji często pojawia się potrzeba powtarzania grupy tych samych instrukcji. W tym przypadku konieczne jest zastosowanie pętli. W języku C++ istnieje do wyboru kilka rodzajów pętli, które są bardziej odpowiednie w różnych zadaniach. Pętle są więc instrukcjami iteracyjnymi, pozwalającymi na cykliczne wykonywanie kolejnych iteracji (cykli).

Każda pętla składa się z dwóch części:
  • treści, czyli jednej bądź więcej instrukcji, które są wykonywane podczas każdego przejścia pętli,
  • warunku, czyli wyrażenia logicznego, które steruje pracą pętli.
W tym rozdziale omówimy pętlę while, którą możemy zaliczyć do pętli o nieznanej (z góry) liczbie cykli. 

Wybierz wartość zmiennej n i zobacz, jak działa pętla while w C++ z animacją:

Składnia instrukcji while jest następująca:

while (warunek)
    instrukcja;

Analogicznie jak w przypadku instrukcji warunkowej if w przypadku pętli while wykonuje się jedna instrukcja. Jeżeli chcemy, aby w pętli wykonywało się kilka instrukcji musimy zastosować nawiasy klamrowe { }, aby uczynić z nich jedną instrukcję złożoną:


while (warunek)
{
    instrukcja_1;
    instrukcja_2;
    ...
    instrukcja_n;
} ;
    

Poniżej przedstawiono prostą symulację pokazującą w jaki sposób działa pętla while.


int n = ...;  // Aktualna wartość: ...
while (n > 0) {
    cout << "n = " << n << endl;
    n--;
}
   
Możemy zaobserwować, że w przypadku pętli while warunek jest sprawdzany na początku. Może więc się zdarzyć, że pętla nie zostanie wywołana ani razu, bo warunek już na początku nie będzie spełniony. Musimy więc zadbać o prawidłową wartość początkową zmiennych występujących w warunku. Możemy to zademonstrować na podstawie prostego przykładu wypisującego liczby całkowite podane przez użytkownika, aż do momentu gdy użytkownik poda wartość 0.
int n = 1;
while (n!=0){
    cin >> n;
    cout << n;
}
W podanym przykładzie ważna jest wartość początkowa zmiennej n. W przypadku, gdy chcemy, aby pętla wykonała się przynajmniej raz musimy określić wartość pocżatkową zmiennej n w taki sposób, aby przynajmniej raz warunek był spełniony. Później już użytkownik bedzie podawał kolejne wartości n i od niego zależy czy warunek będzie spełniony, czy nie.

4. Pętla do while

W celu powtórzenia bloku instrukcji możemy wykorzystać także pętlę do-while. Instrukcja do-while składa się z dwu części: znacznika początku pętli, którym jest właśnie słowo do oraz znacznika jej końca, którym jest słowo while. Dodatkowo, za słowem while występuje warunek wykonywania  pętli.
Składnia instrukcji do-while jest następująca:

do
    instrukcja;
while (warunek);
Analogicznie jak w przypadku pętli while możemy także wykonać kilka instrukcji w pętli do-while. W tym celu możemy zastosować nawiasy klamrowe.

do
{
    instrukcja_1;
    instrukcja_2;
    ...
    instrukcja_n;
} while (warunek);

Główna różnica pomiędzy pętlą while, a do-while jest moment w którym sprawdzany jest warunek. W pętli while warunek jest sprawdzany na początku, natomiast w przypadku pętli do-while warunek sprawdzany jest na końcu. Oznacza to, że pętla do-while wykona się przynajmniej  raz.
Pętla typu do-while zawsze wykona się co najmniej jeden raz, podczas gdy pętla while może nie wykonać się ani razu.
Poniżej przedstawiono prostą symulację działania pętli do-while.

Wybierz wartość zmiennej n i zobacz, jak działa pętla do-while w C++ z animacją:

int n = ...;  // Aktualna wartość: ...
do {
    cout << "n = " << n << endl;
    n--;
} while (n > 0);  // Warunek pętli: n > 0
            

5. Pętla for

Kolejnym rodzajem pętli w języku C++ jest pętla for. Omówione w poprzednich rozdziałach pętle while oraz do-while są wykorzystywane w sytuacjach gdy nie jest znana (z góry) liczba cykli. Pętla for jest wykorzystywana głównie w sytuacjach, gdy wiemy ile chcemy wykonać iteracji pętli. Instrukcja for należy do grupy instrukcji strukturalnych. Aby dokładnie zilustrować w jakim celu możemy wykorzystać pętlę for przedstawimy prosty przykład.
int n = 5;
do{
    cout << n << endl;
    n--;
}while(n=>0);
W przykładowym kodzie za pomocą pętli generowane są liczby zaczynając od wartości 5 do wartości 1. Wszystkie liczby całkowite od 5 do 1 są drukowane na konsoli. Pętla wykonuje się dokładnie 5 razy. Często w trakcie projektowania oraz implementacji aplikacji istnieje potrzeba zdefiniowania pętli, która będzie wykonywała się określoną liczbę razy. Jak widzimy możemy to zrobić przy wykorzystaniu pętli do-while. Pętla for upraszcza wykonanie tego typu operacji i została wprowadzona, aby ułatwić pracę programistów.
Pętlę typu for zapisujemy w C++ następująco:

for (inicjalizacja; warunek końca; krok)
    instrukcja;
Pętla for w języku C++ jest bardzo elastyczna, i  składa się z czterech instrukcji:
  1. Inicjalizacja to instrukcja, która jest wykonywana w momencie rozpoczynania pętli.
  2. Warunek to wyrażenie, które jest sprawdzane przed wykonaniem każdego przebiegu pętli.
  3. Instrukcja to właściwa treść pętli. Jest ona wykonywana w każdym przebiegu pętli.
  4. Krok jest instrukcją wykonywaną po wykonaniu treści pętli.
Kolejność wykonywania poszczególnych instrukcji pokazane jest na rysunku poniżej.



Wszystkie części pętli for mogą, ale nie muszą wystąpić. Konieczne jest tylko postawienie średników, które oddzielają od siebie poszczególne części. Jeśli nie występuje warunek, to traktowane jest to jak wyrażenie zawsze prawdziwe . Tak więc zupełnie prawidłowy jest zapis:
for (;;)
{
    ...
}
Taki opis oznacza, że pętla jest nieskończona. Jest odpowiednikiem pętli while lub do-while w których warunek jest zawsze spełniony.

W pętli for istnieje możliwość zastosowania wielu instrukcji w miejsce inicjacja czy krok, wystarczy oddzielić je przecinkami. Dzieje się tak dlatego, że są one traktowane jako wyrażenia, a wyrażenia można łączyć przecinkami.

Zmienna sterująca (i każda inna zmienna) zdefiniowana w pętli for jest dostępna tylko w obszarze tej pętli.
Podobnie jak w przypadku poprzednio omówionych typów pętli w treści pętli występuje tylko jedna instrukcji. Analogicznie jak poprzednio możliwe jest zastosowanie instrukcji złożonej za pomocą nawiasów klamrowych {}

Poniżej przedstawiono symulację prostej pętli for.

Wybierz wartość zmiennej n i zobacz, jak działa pętla for w C++ z animacją:

for (int i = 0; i < ...; i++) {
    cout << "i = " << i << endl;
}