C Language
Instrukcje / pętle iteracyjne: for, while, do-while
Szukaj…
Składnia
- /* wszystkie wersje */
- for ([wyrażenie]; [wyrażenie]; [wyrażenie]) one_statement
- for ([wyrażenie]; [wyrażenie]; [wyrażenie]) {zero lub kilka instrukcji}
- while (wyrażenie) one_statement
- while (wyrażenie) {zero lub kilka instrukcji}
- wykonaj one_statement while (wyrażenie);
- do {jedna lub więcej instrukcji} while (wyrażenie);
- // od C99 oprócz powyższego formularza
- for (deklaracja; [wyrażenie]; [wyrażenie]) one_statement;
- for (deklaracja; [wyrażenie]; [wyrażenie]) {zero lub kilka instrukcji}
Uwagi
Instrukcja iteracyjna / Pętle dzielą się na dwie kategorie:
- instrukcja / pętle iteracji sterowane głowicą
- instrukcja / pętle iteracji sterowane stopą
Oświadczenie o iteracji sterowane przez głowę / pętle
for ([<expression>]; [<expression>]; [<expression>]) <statement>
while (<expression>) <statement>
for ([declaration expression]; [expression] [; [expression]]) statement
Oświadczenie / pętle sterowane stopą
do <statement> while (<expression>);
Dla pętli
Aby ponownie wykonać blok kodu, na obrazie pojawia się pętla. Pętli for
należy używać, gdy blok kodu ma być wykonywany określoną liczbę razy. Na przykład, aby wypełnić tablicę rozmiaru n
danymi wejściowymi użytkownika, musimy wykonać scanf()
n
razy.
#include <stddef.h> // for size_t
int array[10]; // array of 10 int
for (size_t i = 0; i < 10; i++) // i starts at 0 and finishes with 9
{
scanf("%d", &array[i]);
}
W ten sposób wywołanie funkcji scanf()
jest wykonywane n
razy (w naszym przykładzie 10 razy), ale jest zapisywane tylko raz.
Tutaj zmienna i
jest indeksem pętli i najlepiej jest ją zadeklarować zgodnie z prezentacją. Typ size_t
( typ rozmiaru ) powinien być używany do wszystkiego, co się liczy lub zapętla obiekty danych.
Ten sposób deklarowania zmiennych wewnątrz for
jest dostępny tylko dla kompilatorów, które zostały zaktualizowane do standardu C99. Jeśli z jakiegoś powodu nadal tkwisz w starszym kompilatorze, możesz zadeklarować indeks pętli przed pętlą for
:
#include <stddef.h> /* for size_t */
size_t i;
int array[10]; /* array of 10 int */
for (i = 0; i < 10; i++) /* i starts at 0 and finishes at 9 */
{
scanf("%d", &array[i]);
}
Podczas pętli
while
pętla jest używana do wykonania kawałek kodu, podczas gdy warunek jest prawdziwy. while
pętla ma być stosowane, gdy blok kodu jest wykonywane różną liczbę razy. Na przykład pokazany kod pobiera dane wejściowe użytkownika, o ile użytkownik wstawi cyfry, które nie są 0
. Jeśli użytkownik wstawi 0
, warunek while nie jest już spełniony, więc wykonanie opuści pętlę i przejdzie do dowolnego kolejnego kodu:
int num = 1;
while (num != 0)
{
scanf("%d", &num);
}
Pętla Do-While
W przeciwieństwie for
i while
pętli do-while
Pętle sprawdzić prawdziwość stanu na koniec pętli, co oznacza, że do
blok będzie wykonać raz, a następnie sprawdzić stan while
w dolnej części bloku. Oznacza to, że pętla „ do-while
while” zawsze będzie działać co najmniej raz.
Na przykład ta pętla „ do-while
while” otrzyma liczby od użytkownika, aż suma tych wartości będzie większa lub równa 50
:
int num, sum;
num = sum = 0;
do
{
scanf("%d", &num);
sum += num;
} while (sum < 50);
pętle do-while
są stosunkowo rzadkie w większości stylów programowania.
Struktura i przepływ kontroli w pętli for
for ([declaration-or-expression]; [expression2]; [expression3])
{
/* body of the loop */
}
W pętli for
warunek pętli ma trzy wyrażenia, wszystkie opcjonalne.
- Pierwsze wyrażenie,
declaration-or-expression
, inicjuje pętlę. Jest wykonywany dokładnie raz na początku pętli.
Może to być deklaracja i inicjalizacja zmiennej pętli lub ogólne wyrażenie. Jeśli jest to deklaracja, zakres deklarowanej zmiennej jest ograniczony przez instrukcję for
.
Historyczne wersje C pozwalały tylko na wyrażenie, a deklaracja zmiennej pętli musiała być umieszczona przed for
.
- Drugie wyrażenie,
expression2
, jest warunkiem testu . Najpierw jest wykonywany po inicjalizacji. Jeśli warunek jesttrue
, formant wchodzi w ciało pętli. Jeśli nie, przesuwa się na zewnątrz korpusu pętli na końcu pętli. Następnie ten warunek jest sprawdzany po każdym wykonaniu treści oraz instrukcji aktualizacji. Gdy jest totrue
, formant przesuwa się z powrotem na początek korpusu pętli. Warunkiem jest zwykle sprawdzenie, ile razy wykonuje się treść pętli. Jest to podstawowy sposób na wyjście z pętli, innym sposobem jest użycie instrukcji skoku . - Trzecie wyrażenie,
expression3
, jest instrukcją aktualizacji . Jest wykonywany po każdym wykonaniu treści pętli. Jest często używany do zwiększania zmiennej zliczającej liczbę wykonań ciała pętli, a ta zmienna nazywana jest iteratorem .
Każde wystąpienie wykonania elementu pętli nazywane jest iteracją .
Przykład:
for(int i = 0; i < 10 ; i++)
{
printf("%d", i);
}
Dane wyjściowe to:
0123456789
W powyższym przykładzie pierwsze i = 0
jest wykonywane, inicjując i
. Następnie sprawdzany jest warunek i < 10
, który uznaje się za true
. Element sterujący wchodzi w ciało pętli i drukowana jest wartość i
. Następnie formant przesuwa się do i++
, aktualizując wartość i
z 0 na 1. Następnie warunek jest ponownie sprawdzany i proces jest kontynuowany. Trwa to do momentu, aż wartość i
osiągnie 10. Następnie warunek i < 10
ocenia false
, po czym kontrola wychodzi z pętli.
Nieskończone pętle
Mówi się, że pętla jest nieskończoną pętlą, jeśli element sterujący wejdzie, ale nigdy nie opuści ciała pętli. Dzieje się tak, gdy warunek testowy pętli nigdy nie jest równy false
.
Przykład:
for (int i = 0; i >= 0; )
{
/* body of the loop where i is not changed*/
}
W powyższym przykładzie, zmienna i
, iterator, jest inicjowany na 0. Stan test jest początkowo true
. Jednak i
nie jest modyfikowany w dowolnym miejscu ciała i wyrazem aktualizacja jest pusty. Stąd też i
pozostanie 0, a stan testu nigdy oceni na false
, co prowadzi do nieskończonej pętli.
Zakładając, że nie ma instrukcji skoku, innym sposobem utworzenia nieskończonej pętli jest jawne zachowanie warunku:
while (true)
{
/* body of the loop */
}
W pętli for
instrukcja warunku jest opcjonalna. W takim przypadku warunek jest zawsze true
próżniowo, co prowadzi do nieskończonej pętli.
for (;;)
{
/* body of the loop */
}
Jednak w niektórych przypadkach warunek może być celowo true
, z zamiarem wyjścia z pętli za pomocą instrukcji skoku, takiej jak break
.
while (true)
{
/* statements */
if (condition)
{
/* more statements */
break;
}
}
Rozwijanie pętli i urządzenie Duffa
Czasami prosta pętla nie może być całkowicie zawarta w treści pętli. Wynika to z tego, że pętla musi zostać zainicjowana przez niektóre instrukcje B. Następnie iteracja rozpoczyna się od niektórych instrukcji A , po których następuje powtórka B przed zapętleniem.
do_B();
while (condition) {
do_A();
do_B();
}
Aby uniknąć potencjalnych problemów z wycinaniem / wklejaniem przy dwukrotnym powtarzaniu B w kodzie, można zastosować Urządzenie Duffa, aby uruchomić pętlę od środka ciała while
, używając instrukcji switch i przechodząc przez zachowanie.
switch (true) while (condition) {
case false: do_A(); /* FALL THROUGH */
default: do_B(); /* FALL THROUGH */
}
Urządzenie Duffa zostało faktycznie wynalezione w celu zaimplementowania rozwijania pętli. Wyobraź sobie zastosowanie maski do bloku pamięci, gdzie n
jest znakiem całki ze znakiem o wartości dodatniej.
do {
*ptr++ ^= mask;
} while (--n > 0);
Gdyby n
było zawsze podzielne przez 4, można to łatwo rozwinąć jako:
do {
*ptr++ ^= mask;
*ptr++ ^= mask;
*ptr++ ^= mask;
*ptr++ ^= mask;
} while ((n -= 4) > 0);
Ale w urządzeniu Duffa kod może podążać za rozwijającym się idiomem, który przeskakuje we właściwe miejsce na środku pętli, jeśli n
nie jest podzielne przez 4.
switch (n % 4) do {
case 0: *ptr++ ^= mask; /* FALL THROUGH */
case 3: *ptr++ ^= mask; /* FALL THROUGH */
case 2: *ptr++ ^= mask; /* FALL THROUGH */
case 1: *ptr++ ^= mask; /* FALL THROUGH */
} while ((n -= 4) > 0);
Tego rodzaju ręczne rozwijanie rzadko jest wymagane w nowoczesnych kompilatorach, ponieważ silnik optymalizacji kompilatora może rozwijać pętle w imieniu programisty.