5 Riadiace štruktúry
Základnou jednotkou riadiacej štruktúry je príkaz. Riadiace štruktúry predpisujú poradie vykonávania dielčich výpočtov. Príkazy možno rozdeliť na
- výrazový príkaz
- blok
- podmienený príkaz
- prepínač
- príkaz cyklu
- príkaz skoku
5.1 Výrazový príkaz
Výrazový príkaz vzniká pridaním znaku ";" za výraz (viď kap. 4.5). Pretože výrazom je aj volanie funkcie (napr. štandardnej), v nasledujúcom programe sú tri výrazové príkazy:
/* priklad pr5_1.c výrazové prkazy */
void main(void)
{
int a,b;
a = 1; /* prvy vyrazovy prikaz */
b += 1+3*a; /* druhy vyrazovy prikaz */
printf("a=%d b=%d\n",a,b); /* treti vyrazovy prikaz */
}
5.2 Blok
Blok je tvorený postupnosťou príkazov jazyka C, uzavrenou v dvojici zložených zátvoriek {,}. V bloku je taktiež možné deklarovať lokálne premenné, ktoré majú platnosť iba v rámci daného bloku. Z iných častí programu, resp. blokov nie sú takéto premenné viditeľné. Vnorený blok môže, ale nemusí byť ukončený znakom bodkočiarka. Blok, ktorý obsahuje iba príkazy bez deklarácií premenných, označujeme ako zložený príkaz.
5.3 Podmienený príkaz
Podmienený príkaz umožňuje vykonávať vetvenie priebehu programu. Má dva tvary - skrátený a úplný :
if (výraz) príkaz; /* skrátený */ if (výraz) príkaz1; else príkaz2; /* úplný */
Vetvenie sa vykoná na základe vyhodnotenia podmienky, ktorou je výraz v zátvorkách. Ak je hodnota výrazu nenulová (odpovedá pascalovskému true), vykoná sa príkaz, resp. v prípade úplného tvaru príkaz1. Ak je hodnota výrazu nulová (odpovedá false), vykoná sa iba v druhom prípade príkaz2. Výber minima dvoch čísel môžeme preto zapísať v tvare :
int min(int a, int b) { if (a < b) return(a); else return(b); } alebo int min(int a, int b) { if(a < b) return(a); return(b); } alebo pomocou podmieneného výrazu int min(int a, int b) { return((a < b)? a : b); }
Pri vnáraní podmienených príkazov sa vzťahuje else vždy k najbližšiemu predchádzajúcemu if, preto musíme správne použiť vnorený blok. Príklad :
{ if(y != 0) if (x < 0) x = -x; else printf("Delenie nulou\n") z = x/y; }
nie je správne zapísaný. Musíme použiť vnorený blok alebo zmeniť podmienku prvého príkazu if :
{ if(y != 0) { if (x < 0) x = -x; z = x/y; } else printf("Delenie nulou\n") } resp. { if (y == 0) printf("Delenie nulou\n"); else { x = -x; z = x/y; } }
5.4 Prepínač
Prepínač umožňuje vykonávať vetvenie programu na viac ako dve vetvy. Vetvenie sa rovnako ako v predchádzajúcom príkaze if vykonáva na základe vyhodnotenia výrazu, ktorý je celočíselného typu. Ak sa nájde zhoda medzi hodnotou výrazu a cestou case, pokračuje výpočet touto vetvou (až po príkaz break), inak je skok na default vetvu. Tvar príkazu je :
switch (výraz) { case hodn1 : prikaz1; ... case hodnn : prikazn; default : prikaz; } resp. switch (výraz) { case hodn1 : prikaz1;break ... case hodnn : prikazn;break; default : prikaz;break; }
V prvom prípade sa vykonajú všetky príkazy od zvolenej vetvy až po default, v druhom prípade je výpočet každej vetvy ukončený príkazom break, to znamená že výpočet sa príkazom switch (v Pascale ekvivalent CASE) rozvetví a po jeho ukončení opäť spojí. Spôsob ošetrenia vetvy default záleží na programátorovi.
V uvedenom príklade vytlačí funkcia slovne hodnotu známky, zadanej číslom:
void znslovo(int zn) { switch(zn) { case 1 : printf("vyborne");break; case 2 : printf("velmi dobre");break; case 3 : printf("dobre");break; case 4 : printf("nevyhovel");break; default : printf("nepripustna znamka %d\n",zn);break; } }
5.5 Príkazy cyklu
Príkaz cyklu sa používa pre riadenie opakovania nejakého výpočtu. Určuje spôsob a podmienku ukončenia opakovania. Jazyk C pozná tri varianty príkazu cyklu:
- cyklus while
- cyklus do while
- cyklus for
Cyklus while má tvar :
while (výraz) príkaz;
Výraz tvorí podmienku ukončenia cyklu. Cyklus sa vykonáva, kým je výraz nenulový (true). Telo cyklu sa teda nemusí vykonať ani raz. Príkaz ilustrujeme funkciou na výpočet celočíselnej mocniny celého čísla.
/* priklad pr5_2.c celociselna mocnina pomocou while */
int imoc(int x, int n);
void main(void)
{
printf("Mocnina (%d)%d = %d\n",3,4,imoc(3,4));
}
int imoc(int x, int n)
{
int y=1;
while (n--) y *=x;
return (y);
}
Cyklus do while má tvar :
do príkaz while (výraz);
Výraz tvorí podmienku ukončenia cyklu. Cyklus sa vykonáva, kým je výraz nenulový (true), najmenej však jedenkrát. Použitie príkazu do while je pomerne zriedkavé v porovnaní s ostanými dvoma príkazmi cyklu. Príkaz ilustrujeme rovnakým príkladom funkcie na výpočet celočíselnej mocniny celého čísla :
/* priklad pr5_3.c celociselna mocnina pomocou do while */
int imoc(int x, int n);
void main(void)
{
printf("Mocnina (%d)%d = %d\n",3,4,imoc(3,4));
}
int imoc(int x, int n)
{
int y=1;
do
y *= x;
while (n--) ;
return (y);
}
Cyklus for má tvar :
for (init ; koniec ; zmena) prikaz;
kde init je výraz, ktorý sa vyhodnotí raz na začiatku cyklu, obvykle sa tu nastavujú počiatočné hodnoty cyklu.
Koniec je podmienka ukončenia cyklu (kým je pravdivá - nenulová).
Zmena sa vyčísli po každom vykonaní cyklu, slúži pre zmenu riadiacej premennej cyklu. Táto zmena riadiacej premennej sa nemá uskutočňovať v tele cyklu, ale iba v hlavičke cyklu.
Príkaz ilustrujeme rovnakým príkladom funkcie na výpočet celočíselnej mocniny celého čísla :
/* priklad pr5_4.c celociselna mocnina pomocou for */
int imoc(int x, int n);
void main(void)
{
printf("Mocnina (%d)%d = %d\n",3,4,imoc(3,4));
}
int imoc(int x, int n)
{
int y;
for(y=1; n--; y *= x);
return (y);
}
Často sa používa príkaz for s prázdnym telom, ktorý predstavuje nekonečný cyklus :
for ( ; ; );
Taktiež je možné vynechať v hlavičke cyklu hociktorý (jeden alebo dva) z výrazov init, koniec a zmena. Vynechanie je označené bodkočiarkou, ktorá musí byť prítomná vždy. Vyššie uvedený príklad nekonečného cyklu vlastne predstavuje vynechanie všetkých troch parametrov cyklu. Ukončenie takého cyklu (vyskočenie z tela cyklu) potom musí byť zabezpečené iným spôsobom (viď napr. príkazy skoku).
5.6 Príkazy skoku
Príkazy skoku sa používajú pre ošetrenie, resp. riešenie zvláštnych či výninočných situácií v programe. Ide o situácie, ktoré je spravidla potrebné ošetriť jednorázovo. Medzi príkazy skoku možno zaradiť príkazy :
- break
- continue
- return
- goto
Príkazy break a continue sa používajú predovšetkým v spojení s príkazmi cyklu.
Príkaz break ukončuje najvnútornejšiu neukončenú slučku cyklu a opúšťa okamžite telo cyklu, to znamená, že spôsobuje skok z príkazu cyklu na ďalší príkaz. Ďalšie použitie príkazu break je v príkaze switch (viď kap. 5.4).
Príkaz continue preruší vykonávanie najbližšej nadradenej štruktúrovanej konštrukcie cyklu (najvnútornejšiu neukončenú slučku cyklu) a spôsobí prechod na ďalší krok cyklu bez toho, aby sa predchádzajúci krok cyklu ukončil. Dôležité je, že nespôsobuje skok z tela cyklu von. Použitie príkazov break a continue ilustrujú nasledovné príklady pre čítanie a tlač znaku:
/* priklad pr5_5.c tanie a tla znakov pomocou while */
#include <stdio.h>
void main(void)
{
int c;
while((c = getchar()) < 'z')
{
if (c >= ' ') putchar(c);
}
printf("Koniec citania\n");
}
/* priklad p5_6.c tanie a tla znakov pomocou while, break, continue */
#include <stdio.h>
void main(void)
{
int c;
while (1)
{
if ((c = getchar()) < ' ') continue; /* zahodit neviditelny znak */
if (c == 'z') break; /* koniec po znaku z */
putchar(c);
}
printf("Koniec citania\n");
}
Príkaz return spôsobí ukončenie vykonávania nejakej funkcie a súčasne môže exportovať hodnotu výrazu do nadradenej úrovne ako deklarovaný typ hodnoty funkcie. Môže sa vyskytovať v jednom z tvarov
return výraz; return (výraz); return;
Hodnota výrazu sa stáva hodnotou funkcie (prvé dva tvary príkazu return sú ekvivalentné). Tretí tvar príkazu return sa používa v prípade, keď funkcia je typu void, to znamená, že nevracia žiadnu hodnotu.
Príkaz goto umožňuje vykonať skok tak v rámci jednej funkcie, ako aj medzi jednotlivými funkciami. Je však otázka, aký veľký zmysel takého skoky majú. Obecne je možné vykonať skok z bloku aj do bloku, a samozrejme von z cyklu. Všeobecne sa doporučuje používať príkaz goto čo najmenej a skutočne len vo výnimočných prípadoch. Príkaz, na ktorý sa má uskutočniť skok, musí byť označený návestím, za ktorým nasleduje dvojbodka. Pri skoku do bloku je hodnota všetkých lokálnych premenných daného bloku nedefinovaná. Skok do bloku ilustruje príklad :
/* priklad p5_7.c skok do bloku goto */
#include <stdio.h>
void main(void)
{
int c=5;
goto nav1;
{
int c = 2;
nav1:
printf("c=%d\n", c);
goto nav2;
}
nav2:
printf("c=%d\n", c);
}
Program vytlačí najprv c=2, potom c=5.
5.7 Funkcie
Funkcia predstavuje základnú stavebnú jednotku C jazyka. Obecný tvar funkcie je
Typ výsledku identifikátor(deklarácia argumentov) blok
Typ výsledku aj deklarácia argumentov môžu byť aj prázdne, t.j. typu void. Ak neuvedieme typ funkcie, implicitná hodnota je priradená prekladačom(všeobecne by mala byť int, no nemusí). Typ a počet formálnych a skutočných argumetov funkcie musí byť samozrejme rovnaký (pri volaní). Pretože každá funkcia vracia aspoň jednu hodnotu (okrem void), mal by sa v tele funkcie vyskytovať aspoň jeden príkaz return.
5.8 Príklady
Príkazy cyklov sa využívajú v nasledovných programoch pre prácu so znakmi (čítanie, výpis, počítanie znakov, riadkov, slov, číslic atď).
/* priklad pr5_8.c pocet znakov zadanych z terminalu*/
#include <stdio.h>
void main(void)
{
int nc=0;
while(getchar()!='\n') nc++;
printf("Pocet zadanych znakov = %d\n",nc);
}
/* priklad pr5_9.c pocet cislic, bielych a ostatnych znakov */
#include <stdio.h>
void main(void)
{
int c,i,nwhite,nother;
int ndigit[10];
nwhite=nother=0;
for(i=0;i<10li++) ndigit[i]=0;
while((c=getchar())!='\n'('k'))
{
if(c>='0' && c<='9') ++ndigit[c-'0'];
else if(c==' '||c=='\n'||c=='\t') ++nwhite;
else ++nother;
printf("Pocty zadanych cislic = \n");
for(i=0;i<10;i++) printf("%d",ndigit[i]);
printf("Pocet bielych znakov = %d\n",nwhite);
printf("Pocet ostatnych znakov = %d\n",nother);
}
}
/* priklad pr5_10.c pocet znakov, slov a riadkov */
#include <stdio.h>
#define YES 1
#define NO 0
void main(void)
{
int c,nl,nw,nc,inword;
nw=nc=nl=0;
inword=NO;
while((c=getchar())!='k')
{
nc++;
if(c>=='\n') nl++;
if(c==' '||c=='\n'||c=='\t') inword=NO;
else if (inword==NO)
{
inword=YES;
nw++;
}
}
printf("Pocet riadkov = %d\n",nl);
printf("Pocet slov = %d\n",nw);
printf("Pocet znakov = %d\n",nc);
}
/* priklad pr5_11.c vstup riadkov, tlac najdlhsieho */
#include <stdio.h>
#define MAXLINE 100 /* max. dlzka vstupn. riadku */
int getline(char s[],int lim);
void copy(char s1[], s2[]);
void main(void)
{
int len,max; /* dlzka bezneho a max. riadku */
char line[MAXLINE], save[MAXLINE];
max=0;
while((len=getline(line,MAXLINE))>=0)
{
if (len > max)
{
max=len;
copy(line,save);
}
}
if(max > 0)
printf("Najdlhsi riadok : %s\n",save);
printf("Jeho dlzka : %d\n",max);
}
int getline(char s[],int lim)
{
int c,i;
for(i=0;i<lim-1&&(c=getchar())!='k'&&c!='\n';i++) s[i]=c;
if(c=='\n')
{
s[i]=c;
i++;
}
s[i]='\0';
return(i);
}
void copy(char s1[], s2[])
{
int i=0;
while((s2[i]=s1[i])!='\0') i++;
}
/* priklad pr5_12.c jednoduche umocnenie */
#include <stdio.h>
void main(void)
{
int i;
for(i=0;i<10;i++)
printf(" %d (%d)%d = %d (%d)%d = %d\n",i,2,i,power(2,i),3,i,power(3,i));
}
int power(int x,int n)
{
int i,p=1;
for(i=1;i<=n;i++) p=p*x;
return(p);
}
/*
Iná verzia funkcie power:
int power(int x,int n)
{
int p;
for(p=1;n>0;n--) p=p*x;
return(p);
}
*/