Úvod do programovania v jazyku C

Doc. Ing. Pavel Horovčák, CSc., Prof. RNDr. Igor Podlubný, CSc.

Táto on-line príručka vznikla v rámci riešenia výskumného projektu KEGA č. 112/97 "uniWWWerzita"

  1. Úvod
  2. Preprocesor jazyka C
  3. Premenné
  4. Lexikálne prvky jazyka
  5. Riadiace štruktúry
  6. Štandardný vstup a výstup
  7. Reťazce
  8. Súbory
  9. Smerníky
  10. Datové štruktúry
  11. Triedenie
  12. Terminálové funkcie (QNX)
  13. Doporučená literatúra

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

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 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.

Príklad pr5_2.c :


/* 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 :

Príkazy breakcontinue 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:

Príklad pr5_5.c:


/* 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");
}


Príklad pr5_6.c:


/* 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ď).

Príklad pr5_8.c :


/* 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);
}


Príklad pr5_9.c :


/* 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);
  }
}


Príklad pr5_10.c :


/* 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);
}


Príklad pr5_11.c:


/* 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++;
}


Príklad pr5_12.c:


/* 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);
}
*/