This is an old revision of the document!
while
i do-while
.Priorytet operatorów oznacza to samo, co kolejność działań w matematyce – operator o wyższym priorytecie zostanie wykonany najpierw, a gdy obok siebie stoją, to operacje są wykonywane od lewej do prawej (chyba, że sam operator działa inaczej, o czym za chwilę).
Przykładowo – w matematyce – wyrażenie $(2+2 \cdot 2) \cdot 2$ zostanie obliczone w następujących krokach:
Dlaczego tak? Ponieważ – mówiąc językiem programisty – nawiasy mają najwyższy priorytet, a mnożenie ma wyższy priorytet od dodawania.
Zapoznaj się z tabelą priorytetów operatorów w języku C (uwaga: wielu z zawartych w niej operatorów jeszcze nie znasz, lecz część z nich wprowadzimy sukcesywnie na kolejnych zajęciach).
Zwróć uwagę na kolumnę Associativity, czyli “kierunek wiązania”. Oznacza to, która strona operatora zostanie obliczona w pierwszej kolejności (np. Left-to-right oznacza, że najpierw obliczona zostanie lewa strona operatora) i niejako “w którą stronę działa operator” – przykładowo operator przypisania =
przypisuje wartość stojącą po jego prawej stronie zmiennej stojącej po lewej stronie, zatem jego kierunek wiązania to Right-to-Left.
Tak więc, można stwierdzić, że każde wyrażenie w języku C ma pewną wartość, obliczoną zgodnie z priorytetami i kierunkami wiązania operatorów.
Nie musisz pamiętać całej tej terminologii, lecz musisz kojarzyć o co w tym wszystkim chodzi.
Zadanie 1
Napisz program sprawdzający, czy dla zadanej przez użytkownika liczby $x$ zachodzi $4 < x < 6$.
Przetestuj swój program dla $x = 5$.
Pętla while
wykonuje instrukcję dopóty, dopóki spełniony jest warunek testowy (tzn. wyrażenie podane jako warunek ma wartość niezerową).
Ogólna postać:
while (wyrażenie_testowe) instrukcja
Pętla while
to pętla z warunkiem wejścia, co oznacza że warunek sprawdzany jest przed każdym jej obiegiem (także pierwszym).
Zazwyczaj zechcesz gdzieś wewnątrz pętli zmieniać wartości zmiennych tak, aby warunek wejścia stał się fałszywy :)
Przykład
Obliczanie silni za pomocą pętli while
– porównaj z programem wykonującym to samo zadanie z użyciem pętli for
.
#include <stdio.h> int main() { int s, n, i; printf("Podaj liczbe naturalna n: "); scanf("%d", &n); s = 1; i = 1; while (i <= n) { s *= i; i = i + 1; } printf("%d! = %d\n", n, s); return 0; }
Która pętla Twoim zdaniem lepiej nadaje się do tego zadania?
Dobra praktyka programistyczna mówi:
Stosuj pętlę for
tylko wtedy, gdy dokonujesz zarówno inicjalizacji, jak i aktualizacji. We wszystkich innych przypadkach lepiej (czytelniej) użyć pętli while
.
Zadanie 1
Napisz program obliczający wynik $n \cdot k$ dla liczb całkowitych $n$ i $k$. Użyj pętli while
.
Wskazówka: $n \cdot k = \underbrace{n + n + \ldots + n}_{k \text{ razy}}$
Pętla do-while
, podobnie jak pętla while
wykonuje instrukcję dopóki spełniony jest warunek testowy.
Ogólna postać:
do instrukcja while (wyrażenie_testowe);
Zwróć uwagę na średnik występujący po
while (...)
!
Różnica między dwoma typami pętli while
polega na tym, że do-while
to pętla z warunkiem wyjścia – oznacza to, że warunek sprawdzany jest po każdym obiegu pętli (a więc petla wykona się co najmniej raz).
Przykład
#include <stdio.h> int main(void) { int i = 0; printf("Poczatek petli \n\n"); do { printf("Wartosc zmiennej 'i' wynosi %d \n", i); i = i + 1; } while (i < 5); printf("\nKoniec petli"); return 0; }
W praktyce programiści starają się unikać stosowania do-while
, gdyż:
do-while
pętlą while
.
Uogólnienie powyższej zasady brzmi:
Lepiej stosować pętle z warunkiem wejścia (while
, for
), a nie wyjścia (do-while
). (z tych samych powodów, co podane powyżej)
Zadanie 1
Napisz program, który prosi użytkownika o podanie liczby całkowitej większej od $0$. Program powinien zakończyć działanie dopiero wtedy, gdy użytkownik podał poprawną wartość.
Jedną z zasad tworzenia czytelnego kodu jest umiejętne stosowanie wcięć (indent), które oznaczają “poziom zagłębienia” kodu.
Choć kompilator C pomija znaki białe (a więc m.in. wcięcia i znaki nowej linii), to jednak dla człowieka taki kod byłby ciężki do odczytania.
Zasada brzmi:
Zazwyczaj stosuje się wcięcie szerokości jednego tabulatora bądź czterech spacji.
Przykład:
int main () { --->if (1) { --->--->printf("Hello"); --->--->printf("World!\n"); --->} }
Kwestia formatowania to rzecz umowna – istnieją różne szkoły formatowania, a programiści toczą o to nieustanne “święte wojny”. Najważniejsza zasada brzmi: Stosuj reguły konsekwentnie!
Całą tą “czarną robotę” związaną z formatowaniem może za Ciebie IDE (tu: CodeBlocks).
Aby automatycznie sformatować kod, kliknij prawym przyciskiem myszy na oknie edytora tekstu i wybierz z menu kontekstowego opcję Format use AStyle.
Aby zmienić ustawienia autoformatowania wejdź w Settings → Editor… i z listy z ikonami po lewej wybierz Source formatter.
W przykładach na zajęciach stosuję następujące opcje formatowania:
Zdarza się, że choć program nie zawiera błędów składniowych (czyli kompiluje się bez problemów i da się go uruchomić), to jednak nie działa zgodnie z oczekiwaniami. W takich sytuacjach do zdiagnozowania przyczyny problemu przydaje się narzędzie o nazwie debugger.
Debugger (nazwa czasem tłumaczona na polski jako “odpluskwiacz” ) służy do krokowego wykonania programu (czyli instrukcja po instrukcji), z możliwością podglądu stanu programu (a więc m.in. wartości zmiennych) po każdym kroku.
Debugger nie jest częścią języka – to narzędzie dostarczane (zazwyczaj) w ramach zintegrowanego środowiska programistycznego.
Podczas procesu debuggowania korzysta się z breakpoint-ów, które mówią debuggerowi tyle – “koniecznie zatrzymaj się tutaj”.
(Ciekawi Cię, skąd się wzięła nazwa bug? – Przeczytaj w wolnej chwili ten krótki fragment.)
Do nauki wykorzystaj poniższy kod:
#include <stdio.h> #include <stdlib.h> int main() { int i; int n = 2; printf("Zaczynamy...\n"); for (i = 1; i < 5; i++) { n = n * n; } printf("Wynik = %d\n", n); }
(W razie problemów z uruchomieniem debuggera zaglądnij do FAQ.)
Debuggowanie krok po kroku:
printf()
i wybierz Debug → Toggle breakpoint – po lewej stronie, koło numeru wiersza, pojawi się czerwona kropka (symbol breakpoint-u).
W oknie Watches w pierwszej kolumnie możesz podać w wolnym polu u dołu dowolne poprawne wyrażenie – jego wartość zostanie automatycznie obliczona zgodnie z bieżącym stanem programu.
Dodaj wyrażenie x + 8 < 20
i ponownie prześledź zmianę wartości tego wyrażenia podczas kolejnych obiegów pętli.
Czasem interesuje Cię podgląd stanu programu tylko w kilku określonych miejscach – dodaj w nich breakpointy, a następnie przechodź pomiędzy nimi (czyli wykonuj automatycznie wszystkie instrukcje pomiędzy obecnym, a kolejnym breakpointem) za pomocą Debug → Start/Continue.
Dodaj drugi breakpoint przy drugim wywołaniu printf()
. Uruchom debuggowanie, a następnie ponownie wybierz Debug → Start/Continue.
Pod kątem dalszych studiów na kierunku Inżynieria Biomedyczna zapamiętaj – MATLAB także posiada debugger!
Napisz program, który wypisze na ekran taki fragment:
1 2 3 2 3 1 3 1 2
Użyj pętli for
.
Wskazówka: Pętle, podobnie jak instrukcje warunkowe, można zagnieżdżać. Pętla wewnętrzna wykonuje swój pełny zakres powtórzeń w każdym cyklu pętli zewnętrznej.
(poziom trudności: )
Napisz program, który dla zadanej liczby całkowitej obliczy sumę jej cyfr.
Przykład: dla $196$ poprawnym wynikiem jest $1 + 9 + 6 = 16$
Wskazówka: Wykorzystaj własność dzielenia i obcięcia w języku C.
(poziom trudności: )
Napisz program obliczający pierwiastek kwadratowy zadanej liczby metodą babilońską. Przerwij obliczenia, gdy różnica między kolejnymi przybliżeniami jest mniejsza niż $10^{-4}$.
Wskazówka: Jako pierwsze oszacowanie możesz przyjąć $\sqrt{S} \approx S$
Być może zastanawiasz się: Po co ręcznie obliczać pierwiastek? Przecież można skorzystać z wbudowanej funkcji sqrt()!
Owszem, lecz jeśli nie potrzebujesz mega-dużej dokładności, to metodą babilońską uzyskasz wystarczająco dobry wynik o wiele, wiele szybciej.