Anglická verze
logolink

< Zpět na seznam lekcí

Procedury a funkce

AlgortimyObsah lekce:

  • Co je to procedura
  • Syntaxe procedury
  • Použití procedur
  • Procedura pro součet čísel
  • Procedura pro součet čísel s použitím parametru

Co je to procedura

Procedura je samostatná část programu řešící určitý úkol (viz termín procedurální programování). Procedury se použíají zejména v těchto případech:

  1. chceme-li program zpřehlednit rozložením jeho celého kódu na části (procedury),
  2. opakuje-li se v programu jeden složitější příkaz či poslopnost příkazů,
  3. nějakou část kódu si přejeme používat i v dalších programech.

V jazyce C, narozdíl od programovacího jazyku Pascal, procedury neexistují. Existují zde pouzr funkce. Pokud bychom chtěli dosáhnout stejného efektu jako u procedur, je možné použít funkci bez návratové hodnoty. V dalším textu se proto budeme držet pojmu funkce bez návratové hodnoty.

Syntaxe funkce bez návratové hodnoty

Zápis procedury se skládá z hlavičky (klíčové slovo void, název funkce a případně v závorce skupina parametrů procedury). Následují složené závorky, ve kterých lze zapsat jednotlivé příkazy funkce, popřípadě zde lze definovat lokální proměnné (ty které budou viditelné pouze uvnitř funkce).

Syntaxe funkce bez návratové hodnoty
void název (parametry funkce); //parametry jsou volitelné
{
  programový kód funkce
}

Použití funkcí bez návratové hodnoty

Uvažujme program z předchozí lekce o dvojrozměrném poli, který uměl načíst zvolený počet čísel a vypsat je.

Program pro zadání a výpis čísel
 #include "stdafx.h"
 #define N 5000
 int _tmain(int argc, _TCHAR* argv[])
 {
   int i, pocet, p[N];
   printf("Zadej pocet vkladanych cisel (1- %d)\n",n);
   scanf("%d", &pocet);
   while ((pocet<1) || (pocet>n))
   {
    printf("Zadej znovu:");
    scanf("%d", &pocet);
   }
  for (i=0; i < pocet; i++)
  {
    scanf("%d", &p[i]);
  }
  for (i=0; i < pocet; i++)
  {
    printf("%d\n", p[i]);
  }
  fflush(stdin);
  getchar();
  return 0;
 }

Tento program lze funkčně rozdělit na následující posloupnost kroků:

  1. zadání počtu vkládaných čísel,
  2. vkládání zadaných čísel do pole,
  3. výpis zadaných čísel z pole.

Program je sice poměrně krátký, ale když jej uvidíme poprvé, pak nám bude chvíli trvat než pochopíme, k čemu program slouží a odhalíme princip a základní kroky v jeho funkcionalitě. Toto je z praktického hlediska problém zejména v případě, že vzniká větší program v týmu programátorů. Ti by měli psát program tak, aby mu pokud možno rozumněl v krátké době kdokoli ze zúčastněných. Pojďme tedy program rozložit na jednotlivé kroky.

Rozložení na kroky bude znamenat přepis (přeskládání) jednotlivých částí programu do funkcí:

  1. funkce zadanipoctucisel - zadání počtu vkládaných čísel,
  2. funkce vlozenicisel vkládání zadaných čísel do pole,
  3. funkce vypiscisel výpis zadaných čísel z pole.
Funkce zadanipoctucisel
 void zadanipoctucisel()
 {
   printf("Zadej pocet vkladanych cisel (1-%d)",n);
   scanf("%d", &pocet);
   while ((pocet<1) || (pocet>n))
   {
    printf("Zadej znovu:");
    scanf("%d", &pocet);
   }
 }
Funkce vlozenicisel
 void vlozenicisel()
 {
  for (i=0; i < pocet; i++)
  {
    scanf("%d", &p[i]);
  }
 }
Funkce vypiscisel
 void vypiscisel()
 {
  for (i=0; i < pocet; i++)
  {
    printf("%d\n", p[i]);
  }
 }

Tímto rozkladem na dílčí kroky jsme získali tři jednoduché funkce (programy), ze kterých je teď možno složit celý přehledný program. Funkce přesuneme nad funkci main hlavního programu a v hlavním funkci místo daného kódu funkce jen napíšeme na požadovaném místě její jméno, což způsobí vykonání všech instrukcí obsažených ve funkci stejně jako kdyby byly napsány v místě volání procedury v hlavním programu.

Protože však budeme používat proměnné ve všech funkcích, nebudeme je definovat lokálně ve funkci main, ale globálně před definicí jednotlivých funkcí.

Kompletní program složený z funkcí
 #include "stdafx.h"
 #define N 5000

int i, pocet, p[N];

 void zadanipoctucisel()
 {
   printf("Zadej pocet vkladanych cisel (1-%d)",n);
   scanf("%d", &pocet);
   while ((pocet<1) || (pocet>n))
   {
    printf("Zadej znovu:");
    scanf("%d", &pocet);
   }
 }

 void vlozenicisel()
 {
  for (i=0; i < pocet; i++)
  {
    scanf("%d", &p[i]);
  }
 }

 void vypiscisel()
 {
  for (i=0; i < pocet; i++)
  {
    printf("%d\n", p[i]);
  }
 }

 int _tmain(int argc, _TCHAR* argv[])
 {
  zadanipoctucisel();
  vlozenicisel();
  vypiscisel();
  fflush(stdin);
  getchar();
  return 0;
 }

Vidíme, že program obsahuje hlavičku s globálně definovanými proměnnými, pak deklarace jednotlivých funkcí a nakonec hlavní funkce, která podle potřeby funkce spouští. Hlavní program samozřejmě může obsahovat libovolné další příkazy (nejen volání funkcí).

Výsledkem našeho úsilí je program, v jehož těle jsou pouze tři pokyny: zadanipoctucisel, vlozenicisel, vypiscisel. Je tak velmi rychle jasné, co se v celém programu děje a v případě modifikace některého z těchto kroků již dojde k modifikaci pouze patřičné funkce. Na tomto příkladu jsme si ukázali první ze tří situací, kdy se nejčastěji vyplatí použít funkce: chceme-li program zpřehlednit rozložením jeho celého kódu na části (funkce)).

Funkce pro součet čísel

Pojďme si dále ilustrovat druhou možnost využití funkcí (opakuje-li se v programu jeden složitější příkaz či posloupnost příkazů). Uvažujme jednoduchý program pro součet dvou uživatelem zadaných čísel (místo jednoduchého součtu by funkce mohla realizovat daleko složitější úlohu jako například výpočet nějakého složitého vzorce).

Program pro součet čísel
 #include "stdafx.h"
 int a, b;

 int _tmain(int argc, _TCHAR* argv[])
 {
  printf("Zadejte prosim cislo a: ");
  scanf("%d", &a);
  printf("Zadejte prosim cislo b: ");
  scanf("%d", &b);
  printf("Součet čísel %d a %d je %d.\n", a, b ,a+b);
  fflush(stdin);
  getchar();
  return 0;
 }

Cvičení

Pokuste se tento program popsat slovně v krocích a následně jej rozložte do funkcí jako v předchozím případě.

Řešení cvičení

Tento program lze funkčně rozdělit na následující posloupnost kroků (varianta A):

  1. zadání čísel a a b,
  2. výpočet součtu čísel a a b.

případně varianta B:

  1. zadání čísla a,
  2. zadání čísla b,
  3. výpočet součtu čísel a a b.

Ukažme si další postup na obou variantách, které porovnáme.

Varianta A

Funkce zadanicisel
 void zadanicisel()
 {
  printf("Zadejte prosim cislo a: ");
  scanf("%d", &a);
  printf("Zadejte prosim cislo b: ");
  scanf("%d", &b);
 }
Funkce vypocetsouctucisel
 void vypocetsouctucisel()
 {
  printf("Součet čísel %d a %d je %d\n", a, b, a+b);
 }

Varianta B

Funkce zadanicislaa
 void zadanicislaa()
 {
  printf("Zadejte prosim cislo a: ");
  scanf("%d", &a);
 }
Funkce zadanicislab
 void zadanicislab()
 {
  printf("Zadejte prosim cislo b: ");
  scanf("%d", &b);
 }
Funkce vypocetsouctucisel
 void vypocetsouctucisel()
 {
  writeln("Součet čísel %d a %d je %d\n", a, b, a+b);
 };

Která z variant se vám zdá vhodnější? Proč?

Funkce pro součet čísel s použitím parametru

Ať už jsme se rozhodli pro řešení A nebo B, tak se nám v programu vyskytuje kód, který se opakuje. To není vhodné, neboť v případě potřeby upravit v programu proces zadávání čísel je nutno vše opravovat 2x (případně i vícekrát - záleží na tom, kolikrát jsme kód pro zadávání opakovali). Snadno se tak stane, že program upravíme jen v některých částech a tak se program stává přinejmenším nejednotným a proces úpravy zdlouhavým (musíme jej celý procházet a hledat všechna místa nebo obdobné funkce, kde vyžadujeme úpravu). Pokud bychom například chtěli místo "Zadejte prosim cislo b:", aby program napsal jen "Zadejte pozadovane cislo:", pak musíme tuto skutečnost opravit na dvou řádcích funkce ve variantě A nebo v obou procedurách varianty B. V každém případě tak stejnou věc řešíme na dvou (případně více) místech.

Cvičení

Pokuste se upravit předchozí program (libovolnou ze dvou variant) tak, že program načte dvě různé dvojice čísel (řekněme dvojici a,b a dvojici c,d). Následně program spočítá součty obou dvojic (spočte tedy číslo a+b a číslo c+d).

Řešení cvičení

Výsledný program může vypadat třeba nějak takto:

Varianta A

Program pro zadání a součet dvou dvojic čísel
 #include "stdafx.h"
 int a, b, c, d;

 void zadanicisel()
 {
 printf("Zadejte prosim cislo a: ");
 scanf("%d", &a);
 printf("Zadejte prosim cislo b: ");
 scanf("%d", &b);
 printf("Zadejte prosim cislo c: ");
 scanf("%d", &c);
 printf("Zadejte prosim cislo d: ");
 scanf("%d", &d);
 }

 void vypocetsouctucisel()
 {
 printf("Součet čísel %d a %d je %d.\n",a, b, a+b);
 printf("Součet čísel %d a %d je %d.\n",c, d, c+d);
 };

 int _tmain(int argc, _TCHAR* argv[])
 {
 zadanicisel()
 vypocetsouctucisel()
  fflush(stdin);
  getchar();
  return 0;
 }

Varianta B

Program pro zadání a součet dvou dvojic čísel
 #include "stdafx.h"
 int a, b, c, d;


 void zadaniciselab()
 {
 printf("Zadejte prosim cislo a: ");
 scanf("%d", &a);
 printf("Zadejte prosim cislo b: ");
 scanf("%d", &b);
 }

 void zadaniciselcd()
 {
 printf("Zadejte prosim cislo c: ");
 scanf("%d", &c);
 printf("Zadejte prosim cislo d: ");
 scanf("%d", &d);
 }

 void vypocetsouctucisel()
 {
 printf("Součet čísel %d a %d je %d.\n",a, b, a+b);
 printf("Součet čísel %d a %d je %d.\n",c, d, c+d);
 }

 int _tmain(int argc, _TCHAR* argv[])
 {
 zadanicisel()
 vypocetsouctucisel()
 fflush(stdin);
 getchar();
 return 0;
 }

Podívejme se na uvedené varianty řešení:

Ve variantě A dochází k tomu, že ve funkci zadanicisel se nám 4x opakuje identický pokyn pro zadání čísla (ignorujeme-li označení čísel a,b,c,d což je obecně přípustné) a zároveň se ve funkci vypocetsouctucisel opakuje dvakrát stejný výpočet (pokaždé pouze s jinou dvojicí čísel).

Ve variantě B dochází k obdobným problémům jako v předchozím, jen je první problé rozdělen do dvou v podstatě identických funkcí zadaniciselab a zadaniciselcd.

Program se nám po přepsání do funkcí sice zpřehlednil, ale nahromadily se nám duplicitní posloupnosti příkazů. A představme si program, který by například uměl sečíst naprosto libovolnou dvojici ze zadaných čísel (ab, cd, ca, bd... - celkem 8 možných dvojic). Případně bychom měli například 10 proměnných cislo1,..,cislo10 případně bychom chtěli funkci, která sečte (nebo provede libovolný jiný výpočet) s libovolnými n čísli v poli (které má například 100 prvků). Program by se naustále prodlužoval o všechny možnosti kombinací a postupně by se stal velmi dlouhým, nepřehledným a postupně v podstatě nerealizovatelným (viz požadavek na součet libovolné dvojice čísel v poli o např. 15000 prvcích). Řešení těchto problémů představují tzv. parametry a návratové hodnoty. Parametry budou sloužit pro zadání vstupních hodnot funkce, návratové hodnoty pro předání výsledku nadřazené funkci.

Parametr je proměnná, kterou posíláme do funkce a ta ji použije pro výpočet. Ukažme si příklad:

Program pro zadání a součet dvou dvojic čísel
 #include "stdafx.h"
 int a, b, c, d;


 int zadanicisla();
 {
 int cislo;
 printf("Zadejte prosim cislo: ");
 scanf("%d", &cislo);
 return cislo;
 };

 void vypocetsouctudvoucisel(int cislo1, int cislo2);
 {
 printf("Součet čísel je %d.\n",(cislo1+cislo2));
 };

 int _tmain(int argc, _TCHAR* argv[])
 {
 a = zadanicisla();
 b = zadanicisla();
 vypocetsouctucisel(a,b);
 fflush(stdin);
 getchar();
 return 0;
 }

V hlavičce funkce zadanicisla byl vyměněn typ návratové hodnoty void za int. Tato změna způsobí, že funkce bude vracet celočíselný výsledek. Dále byla vytvořena lokální proměnná cislo, do které bude načtena hodnota a pomocí return cislo; bude předána do cílové proměnné v nadřazené funkci.

Funkce vypocetsouctucisel nebude vracet žádné vstupní hodnoty, bude sloužit pouze pro výpis hodnot, které ji předáme jako vstupní parametry. Vstupní parametry převáváme tak, že v závorce uvedeme datový typ mezera jméno parametru. Pokud je vstupních parametrů více, oddělíme je čárkou.

Program pro zadání a součet dvou dvojic čísel
 #include "stdio.h"
 int a,b,c,d;

 {
 int cislo;
 printf("Zadejte prosim cislo: ");
 scanf("%d", &cislo);
 return cislo;
 };

 void vypocetsouctudvoucisel(int cislo1, int cislo2);
 {
 printf("Součet čísel je %d.\n",(cislo1+cislo2));
 };

 int _tmain(int argc, _TCHAR* argv[])
 {
 a = zadanicisla();
 b = zadanicisla();
 vypocetsouctucisel(a,b);
 c = zadanicisla();
 vypocetsouctucisel(a,c);
 vypocetsouctucisel(c,b);
 fflush(stdin);
 getchar();
 return 0;
 }

Domácí úkol

Pokuste se přepsat program hry piškvorky z minulé lekce do funkcí (účelem je rozdělení programu na jednotlivé části - neberte zde v úvahu parametry).

Domácí úkol

Napište program, který načte od uživatele 5 čísel do pole p1 a do pole p2 . Následně obě pole vypíše a pole p1 setřídí a vypíše znovu po setřízení.

Další čtení

Odkazy
webdesign, xhtml, css, php - Mgr. Michal Mikláš