C/Instrukcje sterujące: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
m Update syntaxhighlight tags - remove use of deprecated <source> tags
Linia 7:
=== if ===
Użycie instrukcji if wygląda tak:
<sourcesyntaxhighlight lang="C">
if (wyrażenie) {
/* blok wykonany, jeśli wyrażenie jest prawdziwe */
}
/* dalsze instrukcje */
</syntaxhighlight>
</source>
Istnieje także możliwość reakcji na nieprawdziwość wyrażenia - wtedy należy zastosować słowo kluczowe '''else''':
<sourcesyntaxhighlight lang="C">
if (wyrażenie) {
/* blok wykonany, jeśli wyrażenie jest prawdziwe */
Linia 21:
}
/* dalsze instrukcje */
</syntaxhighlight>
</source>
Przypatrzmy się bardziej "życiowemu" programowi, który porównuje ze sobą dwie liczby:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
Linia 38:
return 0;
}
</syntaxhighlight>
</source>
 
Stosowany jest też krótszy zapis warunków logicznych, korzystający z tego, jak C rozumie '''[[C/Operatory#"Prawda" i "fałsz" w języku C|prawdę i fałsz]]''', tzn.:
Linia 44:
* liczba całkowita równa zero oznacza fałsz.
 
Jeśli zmienna <tt>a</tt> jest typu <tt>integer</tt>, zamiast: <sourcesyntaxhighlight lang="C"> if (a != 0) b = 1/a;</sourcesyntaxhighlight> można napisać: <sourcesyntaxhighlight lang="C"> if (a) b = 1/a;</sourcesyntaxhighlight>
 
a zamiast <sourcesyntaxhighlight lang="C"> if (a == 0) b = 0; </sourcesyntaxhighlight> można napisać: <sourcesyntaxhighlight lang="C"> if (!a) b = 0;</sourcesyntaxhighlight>
 
 
 
Czasami zamiast pisać instrukcję <tt>if</tt> możemy użyć operatora wyrażenia warunkowego (patrz [[C/Operatory#Operator wyrażenia warunkowego|Operatory]]).
<sourcesyntaxhighlight lang="C">
if (a != 0)
b = 1/a;
else
b = 0;
</syntaxhighlight>
</source>
ma dokładnie taki sam efekt jak:
<sourcesyntaxhighlight lang="C">
b = (a !=0) ? 1/a : 0;
</syntaxhighlight>
</source>
 
=== switch ===
Aby ograniczyć wielokrotne stosowanie instrukcji if możemy użyć '''switch'''. Jej użycie wygląda tak:
<sourcesyntaxhighlight lang="C">
switch (wyrażenie) {
case wartość1: /* instrukcje, jeśli wyrażenie == wartość1 */
Linia 74:
break;
}
</syntaxhighlight>
</source>
Należy pamiętać o użyciu <tt>break</tt> po zakończeniu listy instrukcji następujących po <tt>case</tt>. Jeśli tego nie zrobimy, program przejdzie do wykonywania instrukcji z następnego <tt>case</tt>. Może mieć to fatalne skutki:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
Linia 92:
return 0;
}
</syntaxhighlight>
</source>
A czasami może być celowym zabiegiem (tzw. "fall-through") - wówczas warto zaznaczyć to w komentarzu. Oto przykład:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
Linia 113:
return 0;
}
</syntaxhighlight>
</source>
Przeanalizujmy teraz działający przykład:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
Linia 135:
printf ("Do zapłaty: %d\n", podatek);
}
</syntaxhighlight>
</source>
 
== Pętle ==
Linia 141:
=== while ===
Często zdarza się, że nasz program musi wielokrotnie powtarzać ten sam ciąg instrukcji. Aby nie przepisywać wiele razy tego samego kodu można skorzystać z tzw. '''pętli'''. Pętla wykonuje się dopóty, dopóki prawdziwy jest warunek.
<sourcesyntaxhighlight lang="C">
while (warunek) {
/* instrukcje do wykonania w pętli */
}
/* dalsze instrukcje */
</syntaxhighlight>
</source>
Całą zasadę pętli zrozumiemy lepiej na jakimś działającym przykładzie. Załóżmy, że mamy obliczyć kwadraty liczb od 1 do 10. Piszemy zatem program:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
Linia 160:
return 0;
}
</syntaxhighlight>
</source>
Po analizie kodu mogą nasunąć się dwa pytania:
* Po co zwiększać wartość a o jeden? Otóż gdybyśmy nie dodali instrukcji zwiększającej a, to warunek zawsze byłby spełniony, a pętla "kręciłaby" się w nieskończoność.
Linia 170:
Instrukcję for stosuje się w następujący sposób:
 
<sourcesyntaxhighlight lang="C">
for (wyrażenie1; wyrażenie2; wyrażenie3) {
/* instrukcje do wykonania w pętli */
}
/* dalsze instrukcje */
</syntaxhighlight>
</source>
Jak widać, pętla for znacznie różni się od tego typu pętli, znanych w innych językach programowania.
 
Linia 184:
 
Jeżeli wewnątrz pętli nie ma żadnych instrukcji '''continue''' (opisanych niżej) to jest ona równoważna z:
<sourcesyntaxhighlight lang="C">
{
wyrażenie1;
Linia 193:
}
/* dalsze instrukcje */
</syntaxhighlight>
</source>
Ważną rzeczą jest tutaj to, żeby zrozumieć i zapamiętać jak tak naprawdę działa pętla for. Początkującym programistom nieznajomość tego faktu sprawia wiele problemów.
 
Linia 203:
 
 
<sourcesyntaxhighlight lang="C">
for(i=1; i<=10; ++i){
Linia 220:
printf(" i = %d", i ); // wyrażenie3 i++ zostanie wykonane, nawet jeżeli był to już ostatni obieg pętli
 
</syntaxhighlight>
</source>
Dwa pierwsze przykłady korzystają z własności [[C/Podstawy#Struktura_blokowa|struktury blokowej]], kolejny przykład jest już bardziej wyrafinowany i korzysta z tego, że jako <tt>wyrażenie3</tt> może zostać podane dowolne bardziej skomplikowane wyrażenie, zawierające w sobie inne podwyrażenia. A oto kolejny program, który najpierw wyświetla liczby w kolejności rosnącej, a następnie wraca.
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
int main()
Linia 237:
return 0;
}
</syntaxhighlight>
</source>
Po analizie powyższego kodu, początkujący programista może stwierdzić, że pętla wypisze <tt>123454321</tt>. Stanie się natomiast inaczej. Wynikiem działania powyższego programu będzie ciąg cyfr <tt>12345654321</tt>. Pierwsza pętla wypisze cyfry "12345", lecz po ostatnim swoim obiegu pętla for (tak jak zwykle) [[w:inkrementacja|zinkrementuje]] zmienną <tt>i</tt>. Gdy druga pętla przystąpi do pracy, zacznie ona odliczać począwszy od liczby i=6, a nie 5. By spowodować wyświetlanie liczb od 1 do 5 i z powrotem wystarczy gdzieś między ostatnim obiegiem pierwszej pętli for a pierwszym obiegiem drugiej pętli for zmniejszyć wartość zmiennej <tt>i</tt> o 1.
 
Niech podsumowaniem będzie jakiś działający fragment kodu, który może obliczać wartości kwadratów liczb od 1 do 10.
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
Linia 252:
return 0;
}
</syntaxhighlight>
</source>
{{Porada|W kodzie źródłowym spotyka się często [[C/Operatory#Inkrementacja_i_dekrementacja|inkrementację]] <tt>i++</tt>. Jest to '''zły zwyczaj''', biorący się z wzorowania się na nazwie języka [[C++]]. Post-inkrementacja <tt>i++</tt> powoduje, że tworzony jest obiekt tymczasowy, który jest zwracany jako wynik operacji (choć wynik ten nie jest nigdzie czytany). Jedno kopiowanie liczby do zmiennej tymczasowej nie jest drogie, ale w pętli "for" takie kopiowanie odbywa się po każdym przebiegu pętli. Dodatkowo, w C++ podobną konstrukcję stosuje się do obiektów - kopiowanie obiektu może być już czasochłonną czynnością. Dlatego w pętli "for" należy stosować wyłącznie <tt>++i</tt>.}}
 
=== do..while ===
Pętle while i for mają jeden zasadniczy mankament - może się zdarzyć, że nie wykonają się ani razu. Aby mieć pewność, że nasza pętla będzie miała co najmniej jeden przebieg musimy zastosować pętlę do while. Wygląda ona następująco:
<sourcesyntaxhighlight lang="C">
do {
/* instrukcje do wykonania w pętli */
} while (warunek);
/* dalsze instrukcje */
</syntaxhighlight>
</source>
Zasadniczą różnicą pętli do while jest fakt, iż sprawdza ona warunek pod koniec swojego przebiegu. To właśnie ta cecha decyduje o tym, że pętla wykona się co najmniej raz. A teraz przykład działającego kodu, który tym razem będzie obliczał trzecią potęgę liczb od 1 do 10.
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
Linia 276:
return 0;
}
</syntaxhighlight>
</source>
Może się to wydać zaskakujące, ale również przy tej pętli zamiast bloku instrukcji można zastosować pojedynczą instrukcję, np.:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
Linia 287:
return 0;
}
</syntaxhighlight>
</source>
 
=== break ===
Instrukcja '''break''' pozwala na opuszczenie wykonywania pętli w dowolnym momencie. Przykład użycia:
<sourcesyntaxhighlight lang="C">
int a;
for (a=1 ; a != 9 ; ++a) {
Linia 297:
printf ("%d\n", a);
}
</syntaxhighlight>
</source>
Program wykona tylko 4 przebiegi pętli, gdyż przy 5 przebiegu instrukcja break spowoduje wyjście z pętli.
 
==== Break i pętle nieskończone ====
W przypadku pętli for nie trzeba podawać warunku. W takim przypadku kompilator przyjmie, że warunek jest stale spełniony. Oznacza to, że poniższe pętle są równoważne:
<sourcesyntaxhighlight lang="C">
for (;;) { /* ... */ }
for (;1;) { /* ... */ }
Linia 308:
while (1) { /* ... */ }
do { /* ... */ } while (1);
</syntaxhighlight>
</source>
Takie pętle nazywamy '''pętlami nieskończonymi''', które przerwać może jedynie instrukcja '''break'''<ref>Tak naprawdę podobną operacje, możemy wykonać za pomocą polecenia <tt>goto</tt>. W praktyce jednak stosuje się zasadę, że <tt>break</tt> stosuje się do przerwania działania pętli i wyjścia z niej, <tt>goto</tt> stosuje się natomiast wtedy, kiedy chce się wydostać z kilku zagnieżdżonych pętli za jednym zamachem. Do przerwania pracy pętli mogą nam jeszcze posłużyć polecenia <tt>exit()</tt> lub <tt>return</tt>, ale wówczas zakończymy nie tylko działanie pętli, ale i całego programu/funkcji.</ref>(z racji tego, że warunek pętli zawsze jest prawdziwy) <ref>Żartobliwie można powiedzieć, że stosując pętlę nieskończoną to najlepiej korzystać z pętli <tt>for(;;){}</tt>, gdyż wymaga ona napisania najmniejszej liczby znaków w porównaniu do innych konstrukcji.</ref>.
 
Wszystkie fragmenty kodu działają identycznie:
<sourcesyntaxhighlight lang="C">
int i = 0;
for (;i!=5;++i) {
Linia 328:
++i;
}
</syntaxhighlight>
</source>
 
=== continue ===
W przeciwieństwie do break, która przerywa wykonywanie pętli instrukcja '''continue''' powoduje przejście do następnej iteracji, o ile tylko warunek pętli jest spełniony. Przykład:
<sourcesyntaxhighlight lang="C">
int i;
for (i = 0 ; i < 100 ; ++i) {
Linia 339:
printf ("Koniec\n");
}
</syntaxhighlight>
</source>
Dla wartości i większej od 40 nie będzie wyświetlany komunikat "Koniec". Pętla wykona pełne 100 przejść.
 
 
Oto praktyczny przykład użycia tej instrukcji:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
int main()
Linia 355:
return 0;
}
</syntaxhighlight>
</source>
Powyższy program generuje liczby z zakresu od 1 do 50, które nie są podzielne przez 4.
 
Linia 361:
 
Istnieje także instrukcja, która dokonuje skoku do dowolnego miejsca programu, oznaczonego tzw. '''etykietą'''.
<sourcesyntaxhighlight lang="C">
etykieta:
/* instrukcje */
goto etykieta;
</syntaxhighlight>
</source>
'''Uwaga!''': kompilator GCC w wersji 4.0 i wyższych jest bardzo uczulony na etykiety zamieszczone przed nawiasem klamrowym, zamykającym blok instrukcji. Innymi słowy: niedopuszczalne jest umieszczanie etykiety zaraz przed klamrą, która kończy blok instrukcji, zawartych np. w pętli for. Można natomiast stosować etykietę przed klamrą kończącą daną funkcję.
 
Linia 372:
 
Przykład uzasadnionego użycia:
<sourcesyntaxhighlight lang="C">
int i,j;
for (i = 0; i < 10; ++i) {
Linia 381:
koniec:
/* dalsza czesc programu */
</syntaxhighlight>
</source>
 
Zobacz również : [[Programowanie:C:Biblioteka_standardowa:Indeks_tematyczny#setjmp.h|obsługa nielokalnych skoków]]
Linia 387:
==Natychmiastowe kończenie programu - funkcja exit==
Program może zostać w każdej chwili zakończony - do tego właśnie celu służy funkcja '''exit'''. Używamy jej następująco:
<sourcesyntaxhighlight lang="C">
exit (kod_wyjścia);
</syntaxhighlight>
</source>
Liczba całkowita ''kod_wyjścia'' jest przekazywana do procesu macierzystego, dzięki czemu dostaje on informację, czy program w którym wywołaliśmy tą funkcję zakończył się poprawnie lub czy się tak nie stało. Kody wyjścia są nieustandaryzowane i żeby program był w pełni przenośny należy stosować makra <tt>EXIT_SUCCESS</tt> i <tt>EXIT_FAILURE</tt>, choć na wielu systemach kod 0 oznacza poprawne zakończenie, a kod różny od 0 błędne. W każdym przypadku, jeżeli nasz program potrafi generować wiele różnych kodów, warto je wszystkie udokumentować w ew. dokumentacji. Są one też czasem pomocne przy wykrywaniu błędów.