Instrukcje złożone
while
i do
. | instrukcja_pętli: | |
. | . | while (warunek) instrukcja |
. | . | do instrukcja while (warunek); |
. | . | pętla_for |
. | warunek: | |
. | . | wyrażenie |
Podstawowym narzędziem do opisywania powtarzania wykonywania operacji jest iteracja. W C++ do jej zapisu służą aż trzy instrukcje pętli. Są one takie same jak w C, dwie pierwsze z nich są podobne do pętli (odpowiednio) while i repeat z Pascala.
Instrukcja while
Dopóki warunek jest spełniony, wykonuje podaną instrukcję.
Warunek wylicza się przed każdym wykonaniem instrukcji.
Może nie wykonać ani jednego obrotu.
Instrukcja do
Powtarza wykonywanie instrukcji aż warunek przestanie być spełniony.
Warunek wylicza się po każdym wykonaniu instrukcji.
Zawsze wykonuje co najmniej jeden obrót.
Jak widać semantyka pętli while
i do
w C++ jest typowa.
Znającym Pascala warto zwrócić uwagę, że dozór w pętli do
oznacza warunek kontynuowania
pętli (a nie kończenia jak w repeat z Pascala).
Oto przykłady użycia tych pętli.
Obliczanie największego wspólnego dzielnika dwu liczb naturalnych większych od zera za pomocą odejmowania.
while (m!=n) if(m>n) m=m-n; else n=n-m;
Wypisywanie (od tyłu) cyfr liczby naturalnej nieujemnej.
do{ cout << n%10; n=n/10; } while (n>0);
Warunek
musi być typu logicznego, arytmetycznego lub wskaźnikowego.
Jeśli wartość warunku jest liczbą lub wskaźnikiem, to wartość różną od zera uważa się za warunek prawdziwy, a wartość równą zeru za warunek fałszywy.
Wartość warunku typu innego niż logiczny jest niejawnie przekształcana na typ bool
.
Warunek w pętli while
może być także deklaracją (z pewnymi ograniczeniami), której zasięgiem jest ta pętla,
Ta deklaracja musi zawierać część inicjującą.
Zauważmy, że w pętli do
warunek nie może być deklaracją.
Instrukcja może być deklaracją (jej zasięgiem zawsze jest tylko ta instrukcja). Przy każdym obrocie pętli sterowanie wchodzi i opuszcza ten lokalny zasięg, z wszelkimi tego konsekwencjami.
Zaskakującą cechą pętli w C++ (odziedziczoną po C) jest to, że warunek nie musi mieć typu logicznego (w pierwszych wersjach
języków C i C++ w ogóle nie było takiego typu).
Składnia języka C była tak tworzona, by łatwo zapisywało się w niej typowe programy, natomiast twórcy C nie przykładali
dużej wagi do czytelności programów w tym języku.
Język był przeznaczony dla bardzo zaawansowanych programistów, np. tworzących systemy operacyjne, w związku z tym twórcy języka
uznali, że nie warto udawać przed użytkownikami tego języka, że w pamięci komputera są inne rzeczy niż liczby i adresy —
rzeczy takie jak na przykład wartości logiczne.
Stąd reguła, że każda wartość (liczba lub wskaźnik) różna od zera będzie oznaczała prawdę, a wartość zero fałsz.
W wielu przypadkach taka składnia okazywała się poręczna, na przykład przeglądanie listy za pomocą wskaźnika p
można zapisać
używając dozoru pętli o postaci po prostu p
, czyli:
while (p) p=p->next;(Wskaźniki będą omówione w kolejnych wykładach). Chcąc użyć wartości logicznych należałoby zapisać tę pętlę tak:
while (p != NULL) p=p->next;Niestety za tę kuszącą zwięzłość zapisu płaci się zmniejszoną czytelnością i większym ryzykiem popełniania błędów. Na przykład pominięcie części warunku przy przeglądaniu listy cyklicznej:
while (p) // pominięta część != start p=p->next;zamiast
while (p != start) p=p->next;nie spowoduje żadnego komunikatu ani ostrzeżenia ze strony kompilatora. Niestety nie wszystko to co daje się łatwo zapisać, daje się ławo odczytać.
Deklarowanie zmiennych w dozorach pętli while
nie ma większego praktycznego znaczenia.
Należy pamiętać, że jeśli w treści pętli będzie zadeklarowana (niestatyczna) zmienna, to przy każdym obrocie pętli będzie ona od nowa tworzona (i usuwana na koniec obrotu pętli). Zatem na przykład nie można w kolejnym obrocie pętli odwołać się do wartości nadanej tej zmiennej w poprzednich obrotach pętli. Na przykład program:
i=0; while(i<n){ int j = 0; j++; cout << "* " << j << endl; i++; }Wypisze liczby:
1 1 1 1 1
Ten sam program po dodaniu słowa static
przy deklaracji zmiennej j
:
i=0; while(i<n){ static int j = 0; j++; cout << "* " << j << endl; i++; }Wypisze liczby:
1 2 3 4 5
.
. | pętla_for: | |
. | . | for(inst_ini_for ;) instrukcja |
. | inst_ini_for: | |
. | . | instrukcja_wyrażeniowa |
. | . | prosta_deklaracja |
Warunek jak w poprzednich pętlach,
Pominięcie warunku jest traktowane jako wpisanie true
,
Jeśli instrukcja instr_inic jest deklaracją, to zasięg zadeklarowanych nazw sięga do końca pętli,
Zasięg nazw zadeklarowanych w warunku jest taki sam, jak zasięg nazw zadeklarowanych w inst_ini_for,
Instrukcja może być deklaracją (jej zasięgiem zawsze jest tylko ta instrukcja). Przy każdym obrocie pętli sterowanie wchodzi i opuszcza ten lokalny zasięg.
Instrukcja for jest (praktycznie) równoważna instrukcji:
{ inst_ini_for while ( warunek ) { instrukcja wyrażenie ; } }
Różnica: jeśli w instrukcji wystąpi continue, to wyrażenie
w pętli for
będzie obliczone przed obliczeniem warunku
.
W pętli while nie można pominąć warunku.
break;
continue;
return;
goto identyfikator ;
W C++ zawsze przy wychodzeniu z zasięgu widoczności następuje niszczenie obiektów automatycznych zadeklarowanych w tym zasięgu, w kolejności odwrotnej do ich deklaracji.
Może się pojawić jedynie wewnątrz pętli lub instrukcji wyboru i powoduje przerwanie wykonywania najciaśniej ją otaczającej takiej instrukcji,
Sterowanie przechodzi bezpośrednio za przerwaną instrukcję.
Może się pojawić jedynie wewnątrz instrukcji pętli i powoduje zakończenie bieżącego obrotu (najciaśniej otaczającej) pętli.
Służy do kończenia wykonywania funkcji i (ewentualnie) do przekazywania wartości wyniku funkcji.
Każda funkcja o typie wyniku innym niż void
musi zawierać co najmniej jedną taką instrukcję.
Jeśli typem wyniku funkcji jest void
, to funkcja może nie zawierać żadnej instrukcji return
,
wówczas koniec działania funkcji następuje po dotarciu sterowania do końca treści funkcji.
Nie używamy tej instrukcji.
. | instrukcja_deklaracji: | |
. | . | blok_deklaracji |
Wprowadza do bloku nowy identyfikator.
Ten identyfikator może przesłonić jakiś identyfikator z bloku zewnętrznego (do końca tego bloku).
Inicjowanie zmiennych (auto
i register
) odbywa się przy każdym wykonaniu ich instrukcji deklaracji.
Zmienne te giną przy wychodzeniu z bloku.
Każdy identyfikator musi być najpierw zadeklarowany.
Deklaracja określa typ, może też określać wartość początkową.
Zwykle deklaracja jest też definicją (przydziela pamięć zmiennej, definiuje treść funkcji).
Deklarując nazwę w C++ można podać specyfikator klasy pamięci:
auto
prawie nigdy nie stosowany jawnie (bo jet przyjmowany domyślnie),
register
tyle co auto, z dodatkowym wskazaniem dla kompilatora, że deklarowana zmienna będzie często używana,
static
to słowo ma kilka różnych znaczeń w C++, tu oznacza, że identyfikator będzie zachowywał swoją wartość pomiędzy
kolejnymi wejściami do bloku, w którym jest zadeklarowany,
extern
oznacza, że identyfikator pochodzi z innego pliku, czyli w tym miejscu jest tylko jego deklaracja (żeby kompilator znał np. jego typ,
a definicja (czyli miejsce gdzie została przydzielona pamięć) jest gdzie indziej.
W C++ mamy dwa rodzaje komentarzy:
Komentarze jednowierszowe zaczynające się od //
.
Komentarze (być może) wielowierszowe, zaczynające się od /*
i kończące */
. Te komentarze nie mogą się zagnieżdżać.
Dziesiętne (123543). Ich typem jest pierwszy z wymienionych typów, w którym dają się reprezentować: int, long int, unsigned long int (czyli nigdy nie są typu unsigned int!).
Szesnastkowe (0x3f, 0x4A). Ich typem jest pierwszy z wymienionych typów, w którym dają się reprezentować: int, unsigned int, long int, unsigned long int.
Ósemkowe (0773). Typ j.w.
Przyrostki U, u, L i l do jawnego zapisywania stałych bez znaku i stałych long, przy czym znów jest wybierany najmniejszy typ (zgodny z przyrostkiem), w którym dana wartość się mieści.
Stała 0 jest typu int, ale można jej używać jako stałej (oznaczającej pusty wskaźnik) dowolnego typu wskaźnikowego,
Mają typ double (o ile nie zmienia tego przyrostek)
1.23, 12.223e3, -35E-11,
Przyrostek f, F (float), l, L (long double),
Znak umieszczony w apostrofach ('a'
),
Niektóre znaki są opisane sekwencjami dwu znaków zaczynającymi się od \
. Takich sekwencji jest 13, oto niektóre z nich:
\n
(nowy wiersz),
\\
(lewy ukośnik),
\'
(apostrof),
\ooo
(znak o ósemkowym kodzie ooo, można podać od jednej do trzech cyfr ósemkowych),
\xhhh
(znak o szesnastkowym kodzie hhh, można podać jedną lub więcej cyfr szesnastkowych),
Każda z tych sekwencji opisuje pojedynczy znak!
const char[n]
)Ciąg znaków ujęty w cudzysłów (”ala\n”
).
Zakończony znakiem '\0'
.
Musi się zawierać w jednym wierszu, ale …
… sąsiednie stałe napisowe (nawet z różnych wierszy) są łączone.
true
,
false
.
Identyfikator (nazwa) to ciąg liter i cyfr zaczynający się od litery (_
traktujemy jako literę),
Rozróżnia się duże i małe litery.
Długość nazwy nie jest ograniczona przez C++ (może być ograniczona przez implementację),
Słowo kluczowe C++ nie może być nazwą,
Nazw zaczynających się od _
i dużej litery, bądź zawierających __
(podwójne podkreślenie) nie należy
definiować samemu (są zarezerwowane dla implementacji i standardowych bibliotek).
Nazwa to maksymalny ciąg liter i cyfr.
Treść automatycznie generowana z plików źródłowych LaTeXa za pomocą oprogramowania wykorzystującego LaTeXML.
strona główna | webmaster | o portalu | pomoc
© Wydział Matematyki, Informatyki i Mechaniki UW, 2009-2010. Niniejsze materiały są udostępnione bezpłatnie na licencji Creative Commons Uznanie autorstwa-Użycie niekomercyjne-Bez utworów zależnych 3.0 Polska.
Projekt współfinansowany przez Unię Europejską w ramach Europejskiego Funduszu Społecznego.
Projekt współfinansowany przez Ministerstwo Nauki i Szkolnictwa Wyższego i przez Uniwersytet Warszawski.