3. diel - Pokročilé cykly v jazyku C
V minulej lekcii, Kompilácie v jazyku C a C ++ pokračovanie , sme dokončili téma kompilácie. V lekcii o cykloch z kurzu Základných konštrukcií jazyka C sme si vysvetlili tri základné typy cyklov (while, do while, for) a povedali sme si, ako fungujú. V dnešnom C tutoriálu sa pozrieme na ďalšie príkazy, ktorými možno beh cyklu riadiť. Nakoniec ešte raz rozoberieme for cyklus a ukážeme si nejaké triky, ku ktorým možno využiť.
Continue
Prvým kľúčovým slovom je continue. Ukončí práve vykonávané telo cyklus a skočí na ďalšie iteráciu (ďalší priebeh cyklu). Ukážeme si to napríklad pri vypisovaní pole čísel. Úlohou bude vypísať celé pole okrem položiek, ktoré sú v intervale od 5 do 10. Pôvodným riešením by bolo pridanie podmienky a pri jej splnení by sa číslo nevypísala. Riešenie pomocou continue by vyzeralo nasledovne:
int main(int argc, char** argv) { int cisla[10] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19}; int i; for (i = 0; i < 10; i++) { if (cisla[i] >= 5 && cisla[i] <= 10) continue; printf("%d\n", cisla[i]); } return (EXIT_SUCCESS); }
výsledok:
Continue
1
3
11
13
15
17
19
Všimnime si jednej dôležitej veci. Pri použití continue sa u cykle for vykonala operácia, ktorá sa štandardne vykonáva na konci cyklu (v cykle for sa jedná o tretiu "parameter"). To možno veľmi pekne využiť pre rôzne cyklické operácie. Viac sa tomu budem venovať ďalej v článku v kapitole venovanej práve cyklu for.
Break
Rovnako ako continue, aj break ukončí práve vykonávané telo cyklu. Na rozdiel od continue sa ale ukončí celý cyklus a program pokračuje za cyklom. Predvedieme si to na algoritme Eratosthenova sita. Jedná sa o algoritmus pre hľadanie prvočísel. Prvočísla si budeme ukladať v poli. Postupne budeme prechádzať všetky čísla a narazíme ak na číslo, ktoré nedelí žiadne iné prvočíslo, potom sme narazili na nové prvočíslo a môžeme ho vložiť medzi ostatné. Najskôr uvediem kód a potom uvediem vysvetlenie.
#include <stdio.h> #include <stdlib.h> #define POCET_HLEDAYCH_PRVOCISEL 10 int main(int argc, char** argv) { int prvocisla[POCET_HLEDAYCH_PRVOCISEL] = {2}; //pole pro uložení provočísel int pocet_nalezenych_prvocisel = 1; //počet již nalezených prvočísel int i = 2; //aktuálně zpracovávané číslo int j = 0; //dočasný index while (1) //nekonečný cyklus { //zjistíme, jestli nějaké z prvočísel dělí aktuální číslo for (j = 0; j < pocet_nalezenych_prvocisel; j++) if (i % prvocisla[j] == 0) //našli jsme prvočíslo, které dělí aktuální číslo break; //cyklus for můžeme ukončit - to znamená že podmínka cyklu je stále platní //pokud cyklus doběhl, jedná se o prvočíslo if (j == pocet_nalezenych_prvocisel) { prvocisla[j] = i; pocet_nalezenych_prvocisel++; } //podmínka konce cyklu if (pocet_nalezenych_prvocisel == POCET_HLEDAYCH_PRVOCISEL) break; i++; } //vypíšeme prvočísla for (i = 0; i < pocet_nalezenych_prvocisel; i++) printf("Prvocislo: %d\n", prvocisla[i]); return (EXIT_SUCCESS); }
Hlavnou časťou programu je prvý for cyklus. Ak nájdeme už existujúce prvočíslo, ktoré delia aktuálne číslo, tak cyklus ukončíme. Na rozdiel od continue, pri break neprebehne akcie na konci cyklu. Teda iba v prípade, keď sa nezavolá žiadny break, bude hodnota j rovná počtu nájdených prvočísel (cyklus ukončí jeho podmienka). Toho využívame v ďalšej podmienke. Celý nekonečný cyklus ukončíme vtedy, ak máme už v poli dostatočný počet prvočísel. Potom pole len vypíšeme.
EratesthenovoSito
Prvocislo: 2
Prvocislo: 3
Prvocislo: 5
Prvocislo: 7
Prvocislo: 11
Prvocislo: 13
Prvocislo: 17
Prvocislo: 19
Prvocislo: 23
Prvocislo: 29
Goto
S príkazom goto sa stretnete len veľmi zriedka. Všeobecne sa jedná o zlý prístup k návrhu programu a je to skôr pozostatok z nízkoúrovňových jazykov ako je jazyk symbolických adries. Príkazom goto môžeme skočiť na ľubovoľné miesto v programe. Najprv toto miesto označíme (tzv. Návestí). Potom môžeme zavolať goto na názov návestie. Ukážeme si na úprave predchádzajúceho príkladu, kedy miesto vypisovania prvočísel v cykle použijeme príkaz goto.
//vypíšeme prvočísla i = 0; //počíteční inicializace zacatek_cyklu: //návěstí if( i < pocet_nalezenych_prvocisel) //podmínka cyklu { printf("Prvocislo: %d\n", prvocisla[i]); i++; goto zacatek_cyklu; //skok na začátek cyklu }
Určite sa zhodneme na tom, že použitie for cyklu bolo oveľa elegantnejšie. Čo viac, dokázali by ste rozlúštiť, čo vypíše nasledujúci program?
goto navesti_x; navesti_b: printf(" va"); goto navesti_h; navesti_u: printf(" cha"); goto navesti_z; navesti_h: printf("s z"); goto navesti_u; navesti_d: printf(" programu"); goto navesti_r; navesti_x: printf("Zdravim"); goto navesti_b; navesti_r: return (EXIT_SUCCESS); navesti_z: printf("otickeho"); goto navesti_d;
Určite na prvý pohľad nie. Práve preto sa od goto opúšťa, pretože vždy sa dá tú rovnakú úlohu vyriešiť inak a prehľadnejšie.
Ternárne operátor
Teraz sa len veľmi rýchlo pozrieme na ternárne operátor. Je to jediný operátor v C, ktorý prijíma tri operandy. Syntax je nasledovná:
podmínka ? výraz1 : výraz2;
Ak je podmienka splnená, bude vrátený prvý výraz, inak bude vrátený výraz druhý. Pre predstavu možno program prepísať pomocou podmienok:
if(podmínka) výraz1; else výraz2;
Avšak sa nejedná o plnú rovnocennosti! Ternárne operátor môže byť použitý aj k priradenie.
int maximum = a > b ? a : b; int maximum_func(int a, int b) { return a > b ? a : b; }
Oba výrazy vráti väčšie z hodnôt (ak sú hodnoty ekvivalentnej, je jedno ktorú vrátime). O ternárním operátora môžete skutočne uvažovať tým spôsobom, že niečo vracia. Nič nám ale nebráni zavolať vnútri výrazov funkciu:
void print_max(int a) { printf("Maximum je %d",a); } int main() { int a, b; // ..... a > b ? print_max(a) : print_max(b); }
Ternárne operátor možno aj ponoriť, avšak opäť za cenu zníženia čitateľnosti kódu. Výber najväčšieho z troch čísel by vyzeral nejako takto:
int max = a > b ? a > c ? a : c : b > c ? b : c;
Cyklus for
Teraz sa ešte naposledy vrátim k cyklu for. Ako vieme, skladá sa z troch zložiek. Prvou zložkou je počiatočná inicializácia, druhou zložkou je podmienka a posledná zložkou je akcia po dokončení cyklu. Tieto zložky sa nemusia týkať len samotného cyklu, ale môžete o nich uvažovať v širšom meradle. Prepíšeme si algoritmus Eratosthenova sita do kratšie podoby:
int main(int argc, char** argv) { int prvocisla[POCET_HLEDAYCH_PRVOCISEL] = {2}; //pole pro uložení provočísel int pocet_nalezenych_prvocisel = 1; //počet již nalezených prvočísel int i = 2; //aktuálně zpracovávané číslo int j = 0; //dočasný index while (1) //nekonečný cyklus { //zjistíme, jestli nějaké z prvočísel dělí aktuální číslo for (j = 0; j < pocet_nalezenych_prvocisel && i % prvocisla[j] != 0; j++); //pokud ne tak aktuální číslo přidáme mezi prvočísla if (j == pocet_nalezenych_prvocisel) { prvocisla[j] = i; pocet_nalezenych_prvocisel++; } //podmínka konce cyklu if (pocet_nalezenych_prvocisel == POCET_HLEDAYCH_PRVOCISEL) break; i++; } //vypíšeme prvočísla for (i = 0; i < pocet_nalezenych_prvocisel; printf("Prvocislo: %d\n", prvocisla[i++])); return (EXIT_SUCCESS); }
Všimnime si skrátenie samotných cyklov. Namiesto aby sme použili break na ukončenie cyklu, pridáme samotnú podmienku do vyhodnotenia pokračovania cyklu. Ďalej sa zmenil aj cyklus, ktorý mal na starosti vypisovanie. V operácii "na konci cyklu" sme pridali výpis prvočísla. Ak by sme chceli vykonať operácií niekoľko, jednoducho ich oddelíme čiarkou. Dôležité sú tiež bodkočiarkami za cyklom. Tie hovoria, že cyklus nemá žiadne telo.
V našom prípade to program nezjednodušil. Dokonca sa vo výsledku program číta horšie. Osobne sa mi tento postup osvedčil u štruktúr ako sú napríklad spojové zoznamy. Pre príklad si predstavme jednosmerne reťazcový spojový zoznam. Ako sa dostaneme na koniec zoznamu? Pomocou cyklu for veľmi rýchlo a elegantne.
typedef struct { int val; NODE* dalsi; } NODE; // ... NODE* posledni_node = prvni_node; for(;posledni_node->dalsi != NULL; posledni_node = posledni_node->dalsi);
Cykly (a cyklus for predovšetkým) teda nemusí pracovať len s indexmi - súčasťou cyklu môže byť ľubovoľná operácia. Tiež môžu niektoré časti úplne chýbať. Je praktické uvažovať nad jednotlivými časťami v cykle oveľa všeobecnejšie. V budúcej lekcii, Makrá v programovacom jazyku C , na nás čakajú makrá.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 18x (206.39 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C