Operační systém UNIX a jazyk C

Jan Brodský
Luděk Skočovský

SNTL Nakladatelství technické literatury Praha 1989, ISBN 80-03-00049-1

5. Programovací jazyk C


5.5 Příkazy

Vraťme se ještě k příkazům z odst. 5.1.4, které jsou až na výjimky prováděny sekvenčně.

5.5.1 Prázdný příkaz, výrazový příkaz

Na rozdíl např. od Pascalu, kde přiřazení je příkazem, je přiřazení výrazem. Jazyk C ovšem z libovolného výrazu utvoří příkaz tím, že výraz ukončí středníkem.

Výrazové příkazy typu

sin(alfa)*4;
a[i] + b[i];
jsou sice správně vytvořeny, ale nemají význam.

Výrazy, které mění hodnotu některého z použitých objektů, tedy i všechny přiřazovací výrazy, však význam příkazu mají, např.

x = y*y;
i++;
--i, ++j;

Speciálním případem je prázdný příkaz (tvořený jen středníkem)

;
použitelný jako prázdné tělo cyklu nebo umožňující použít návěští těsně před koncem složeného příkazu.

5.5.2 Složený příkaz bloku

Libovolné příkazy mohou být závorkami {, } uzavřeny do příkazu složeného, se kterým se zachází jako s jedním příkazem. Jsou-li na začátku složeného příkazu uvedeny deklarace, mluvíme o bloku. Platí obvyklá pravidla o vnořených blocích a skrývání deklarací identifikátorů deklarovaných v bloku i v blocích nadřazených (uvnitř bloku nejsou vnější významy vidět a po výstupu z bloku se opět objeví).

Objekty deklarované uvnitř bloku mají implicitně přisouzenou třídu auto, tedy zápisy

{ int i;              {auto int;
   ...                  ...
}                      }
jsou ekvivalentní.

V bloku lze deklarovat i proměnné třídy static jsou pouze definovány (není jim alokována paměť!). To ovlivňuje i možné inicializace. Inicializace objektů třídy auto nebo register jsou provedeny při každém vstupu do bloku přes otevírací závorku '{'. Objekty třídy static jsou inicializovány pouze jednou (při zavední programu do paměti) a externí objekty nemají inicializaci povolenu, protože nemají alokovánu paměť.

V bloku

{ int i=0;
static char m[]= "BLOK UKAZKOVY";
register char *p = m;
extern float x,y;
...
}
jsou proměnné i, p inicializovány při každém vstupu do bloku, pole m pouze jednou. Identifikátor m je v tomto významu viditelný pouze uvnitř bloku. Kdyby byla jeho deklarace uvedena na úrovni externích definic, byl by viditelný ze všech lexikálně následujících funkcí téhož zdrojového souboru.

Koncová závorka } zřetelně ukončuje složený příkaz, takže není třeba za ním psát středník a syntaxe jazyka C to ani nedovoluje.

5.5.3 Podmíněný příkaz

Podmíněný příkaz může mít v C podobu neúplného větvení:

if(E)          if(E)
   S1;            S1;
                  else S2;

V obou případech se nejdříve vyhodnotí podmínkový výraz E uvedený za if. Má-li nenulovo hodnotu (true), provede se příkaz S1 za podmínkovým výrazem (neužívá se klíčové slovo then). Má-li nulovou hodnotu (false), provede se příkaz uvedený za else, tj. S2. To lze využít např. ve funkci

max(a,b)
{
     if(a > b)
          return(a);
      else
          return(b);
}
V případě neúplného příkazu se jednoduše přejde na příkaz následující za if:
{
     if(a > b)
          return(a);
     return(b);
}
kde se využívá toho, že první přákaz za podmínkovým výrazem opustí tělo funkce, takže následující příkaz se provede, jen když není a > b.

Nejednoznačnost v přiřazení if a else u vnořených podmíněných příkazů se řeší přiřazením else k nejblíže předcházejícímu volnému if. To vadí v případě, kdy neúplné větvení je vnořeno jako první příkaz do úplného větvení.

if(y!=0)
    if(x<0)
        x = -x;
    else
        printf("DELENI NULOU?\n");
    z = x/y;

Diagnostická zpráva se chybně vypíše pro zápornou hodnotu x. Správný zápis využívá složeného příkazu (byť z jediného jednoduššího):

if(y!=0)
    {if(x<0)
        x = -x;
     }
    else
        printf("DELENI NULOU?\n");
    z = x/y;

Poměrně často se vyskytuje v programech větvení pro více než dva případy. To lze v C vyjádřit posloupností úplných podmíněných příkazů, např.:

if(odstin == bila)
       index = -5;
else if(odstin == cervena)
       index = 3;
elseindex = 7;

5.5.4 Cykly while a do

Cyklus while ve tvaru

while(E)
    S;
nejprve testuje podmínkový výraz E. Je-li nenulový, provede se příkaz S uvedený za podmínkou a celý postup se opakuje. Při nulové hodnotě činnost cyklu while končí. Tak např.
k = 1, i = n;
while(i--)
    k*=m;
uloží do k n-tou mocninu čísla m.

Cyklus do ve tvaru

do S
while(E);
provede příkaz S (tedy vždy alespoň jednou) a potom vyhodnotí podmínku E. Je-li nenulová, provede se opět příkaz S, jinak činnost cyklu do končí. Předchozí příklad může být zapsán takto:
k = 1, i = n;
do
   k*=m;
while(i--);

Všimněte si rozdílného použití operátoru dekrementace. V prvním případě se vyhodnotí i jako podmínka a pak se hodnota sníží, v druhém případě se hodnota i sníží a pak se vyhodnotí jeho podmínka. Tím se vyrovná rozdíl mezi oběma cykly (pro n > 0).

5.5.5 Cyklus for

Tento neobvyklý cyklus

for(E1; E2; E3)
    S;
se dá převést na cyklus while. Podle definice jazyka C je ekvivalentní se zápisem
E1;
while(E2)
  { S;
      E3;
  }

Za klíčovým slovem for se zapisuje záhlaví cyklu (výrazy E1, E2 a E3 v závorkách jsou odděleny středníky), potom tělo cyklu, příkaz S. Vyhodnotí se E1, otestuje se podmínkový výraz E2. Je-li nenulový, provede se příkaz S a výraz E3 a činnost se opakuje od vyhodnocení podmínkového výrazu E2.

Pro cyklus od 1 do n se užívá např.:

k = 1;
for(i=1; i<=n; i++)
  k*=m;
a může se zapsat i takto
for(k=1, i=n; i--;)
  k*=m;
což je podle definice jazyka C ekvivalentní s
k=1, i=n;
while(i--;)
    k*=m;
a to je úryvek programu z předchozího odstavce.

Všechny výrazy se závorkami mohou chybět, středníky zůstávají a chybějící druhý podmínkový výraz se považuje za trvale pravdivý. Tedy

for(;;)
{
    ...
}
je záhlavím nekonečného cyklu, který musí být opuštěn jinými prostředky (příkazem break, return nebo goto).

Příkaz cyklu for může být použit i v jiných situacích, např. potřebujeme-li ve znakovém poli c najít první výskyt znaku *. Využijeme k tomu ukazatele p na char:

for(p=c; *p!='*'; p++)
  ;
V tomto případě se požadovaná činnost odehraje v záhlaví cyklu a tělo cyklu je tvořeno prázdným příkazem.

5.5.6 Přepínač, příkaz break

Příkaz přepínače je alternativním řešením posloupnosti úplných podmínkových příkazů. Má zpravidla tvar

switch(E)
{
...
}
kde hodnota výrazu E se převede na typ int a použije se pro předání řízeni dovnitř složeného příkazu. Místa předání řízení jsou označena klíčovým slovem case a konstantním výrazem typu int. Řízení se předá do místa označeného stejnou hodnotou jako má výraz E. Nenajde-li se takové místo, hledá se místo označené klíčovým slovem default. Nenajde-li se ani default, končí činnost přepínače. Tak např.
switch(odstin)
{
case bila    : index = -5;
             break;
case cervena : index = 3;
             break;
default      : index = 7;
}
je přepínačová verze úryvku programu z odst. 5.5.3.

Příkazem break končí provádění nejblíže nadřazeného složeného příkazu příslušného k přepínači switch. Není-li použit, pak provádění větve přepínače nekončí nalezením dalšího místa předání řízení, ale pokračuje dále. To je však málokdy výhodné. Je povoleno označit místo předání řízení několika konstantními výrazy, např.:

switch(dnes){
    case PO:
    case UT:
    case ST:
    case CT:
    case PA: pracovni++;
             break;
    case SO: priplatek = 50;
             break;
    case NE: priplatek = 100;
             break;
}

Příkazem break lze opustit i nejblíže nadřazený cyklus while, do nebo for. Tak např. při přepisování posloupnosti nejvýše 80 znaků ukončené novým řádkem ze znakového pole x do znakového pole y můžeme použít cyklus:

for(i=0; i<80; i++)
	{	y[i] = x[i];
		if(x[i] == '\n')
		    break;
	}

5.5.7 Příkaz continue

Tento příkaz se obdobně jako příkaz break pohybuje v kontextu nejblíže nadřazeného cyklu while, do nebo for. Místo, aby způsobil opuštění cyklu, začíná nový průchod cyklem, aniž by se dokončil průchod předchozím. Příklad můžeme přepsat takto:

for(i=0; i<80; i++)
	{	switch(y[i] = x[i]) {
			case '\n' : break;
		 	default   : continue;
		}
		break;
	}

Prvním příkazem break (po nalezení nového řádku) opustíme switch, druhým příkazem break cyklus for. Příkaz continue pro ostatní znaky zahájí nový průchod cyklem a vyhne se tak druhému příkazu break.