[Obsah] [Cvičení 2]

Cvičení 1


Témata


Parametry hlavního programu

Operační systémy umožňují předávat programu při jeho spouštění parametry (argumenty) hlavního programu. Např. notepad.exe text.txt představuje spuštění programu notepad (poznámkový blok) s jedním parametrem - jménem souboru, který má být automaticky otevřen.

Abychom v hlavním programu v jazyce C mohli přistupovat k parametrům, musíme deklarovat hlavičku hlavního programu takto:

int main(int argc, char **argv)
{
 
  ...
  return 0;
}
nebo
int main(int argc, char *argv[])
{
 
  ...
  return 0;
}

Hodnota argc nese počet parametrů na příkazovém řádku. Tento počet je včetně jména spouštěného programu! Pokud bychom programovali poznámkový blok v jazyce C a spouštěli jej notepad.exe text.txt, měl by parametr argc hodnotu 2. V případě spouštění fiktivního programu progr se dvěma parametry, progr nadpis 12, má argc hodnotu 3.

Parametr argv je ukazatel na pole řetězů, které představují parametry příkazové řádky, včetně názvu programu. Tedy, argv[0] je ukazatel na řetězec s názvem spouštěného programu, který může být včetně cesty, záleží na platformě, operačním systému apod. Dále, argv[1] je řetězec s prvním parametem, argv[2] je řetězec s druhým parametrem atd.

Vrátíme-li se k příkladu s poznámkovým blokem a uvažujeme-li standardní instalaci systému Windows, pak argv[0] je řetězec C:\WINDOWS\notepad.exe, argv[1] má hodnotu text.txt.
Ve druhém příkladu s programem prog obsahuje argv[0] název programu (event. včetně cesty), argv[1] obsahuje řetězec nadpis a argv[2] řetězec (!) 12.


Návratový kód hlavního programu

Hlavní program v úvodu kapitoly je deklarován jako funkce s návratovou hodnotou typu int (hlavní program může vracet pouze celé číslo). Každý program může při ukončení předat operačnímu systému (vrátit) číslo, tzv. návratový kód. Platí nepsané pravidlo, že hodnota 0 znamená úspěšné ukončení programu, nenulová hodnota chybu. Význam nenulových návratových hodnot není dán a záleží jen na programátorovi, jak hodnoty využije (měl by vše popsat v dokumentaci programu).

Princip se využívá zejména v dávkových souborech (typu .bat, skriptech v UNIXu/Linuxu), kdy po skončení programu je možné větvit algoritmus dávky právě podle návratového kódu (test proměnné errorlevel).

Principiálně vypadá programování návratových kódu takto:

int main(int argc, char **argv)
{
  kód;
  if (chyba) return 1;
  kód;
  return 0;
}
Pokud programátor nehodlá využívat parametry hlavního programu, ani vracet hodnotu, může deklarovat hlavní program jako proceduru bez parametrů:
void main(void)
{
  ...
}

Úloha 1.1

Napište jednoduchou kalkulačku. Program s názvem kalk bude mít povinně 3 parametry: číslo operátor, číslo, např. kalk 3.5 + 2. Na obrazovku pak vypíše výsledek operace. Uvažujte tyto operátory: + - * /. Ošetřete chybu dělení nulou a správný počet parametrů programu. Není-li počet argumentů správný, vypište chybové hlášení ve tvaru:
Spouštění: kalk číslo operátor číslo
Jméno programu kalk získejte z argumentu argv[0], odstraňte cestu.
(Návod: číselné hodnoty z řetězců převádějte pomocí funkce sscanf, jméno programu nekopírujte - najděte poslední výskyt znaku '\\' a při tisku jména programu předejte ukazatel na další znak.)

Polotovar:

Dev C++: kalkp.dev, kalkp.c
CodeBlocks: kalkp.cbp, kalkp.c

Řešení:

Dev C++:kalk.dev, kalk.c
CodeBlocks:kalk.cbp, kalk.c


Definice uživatelského typu

Pomocí klíčového slova typedef lze definovat vlastní typ.

Obecně má definice typu podobu:

typedef definice_typu identifikátor_typu;
Jako první příklad uvedeme vytvoření de facto aliasu typu float:
typedef float TPrvek;
Definovali jsme nový typ TPrvek, který je evivalentem typu float. Čtenář si možná klade otázku, proč jsme uvedli tento jednoduchý příklad, jenž se může zdát nesmyslným. Nesmyslný příklad není, naopak, ukazuje možnost, jak psát programy odolnější proti chybám. Představme si, že zpracováváme dynamické pole prvků typu int. V kódu se určitě vyskytují tyto řádky:
int main()
{
  int *pole;
  ...
  pole = (int *)malloc(n*sizeof(int));
  ...
}
Pokud se rozhodneme pracovat s prvky typu float, musíme přepsat typ int všude tam, kde se s prvky pole pracuje (zde na dvou místech kódu - v deklaraci pole a při volání funkce malloc). Je nebezpečí, že někde v kódu zapomeneme typy zaměnit nebo zaměníme omylem typy v té části kódu, která se prvků pole vůbec netýká (např. hlavičky procedur a funkcí).

Pokud nadefinujeme uživatelský typ a dodržujeme důsledně jeho použití v kódu všude tam, kde se pracuje s polem, stačí pak upravit pouze definici typu:

typedef int TPrvek;

int main()
{
  TPrvek *pole;
  ...
  pole = (TPrvek *)malloc(n*sizeof(TPrvek));
  ...
}
Ukážeme si ještě dva příklady definice typů:
typedef int* TPint; // typ TPint je typ ukazatel na int, proměnnou pi typu ukazatel pak deklarujeme: TPint pi;
typedef float TPoledeset[10]; // TPoledeset je typ pole o deseti prvcích typu float, vlastní pole A pak deklarujeme: TPoledeset A;
Poznámka: Bývá zvykem, že identifikátory uživatelsky definovaných typů programátoři začínají písmenem T (jako typ)

Výčtový datový typ

Výčtový (enumerační) datový typ je typ, jehož obor hodnot je dán (jak již název napovídá) výčtem těchto hodnot. Hodnoty jsou specifikovány symbolickými jmény. Nadefinujme výčtový typ reprezentující barvy:
typedef  enum { CERVENA, MODRA, ZELENA, BILA, CERNA } TBarvy;
Obsahem proměnné typu TBarva jsou tedy hodnoty CERVENA, MODRA, ZELENA, BILA nebo CERNA.

Proměnnou barva_auta typu TBarvy nadeklarujeme standardně:

TBarvy barva_auta;
V kódu pracujeme se proměnnými výčtového typy a symbolickými hodnotami běžně jako s jinými standardními datovými typy , např.:
barva_auta = CERVENA;
if (barva_auta == CERVENA) ...
V jazyce C jsou hodnoty výčtového datového typu interně reprezentovány celými čísly počínaje 0, tj. v našem příkladě s barvami je hodnota CERVENA reprezentována 0, MODRA 1 atd. Při definici typu máme možnost ovlivnit vnitřní reprezentaci přiřazením hodnot:
typedef  enum { CERVENA, MODRA=5, ZELENA, BILA=10, CERNA } TBarvy;
V tomto případě je hodnota CERVENA vnitřně reprezentována 0, MODRA 5, ZELENA 6, BILA 10, CERNA 11. Překladač přiřazení nekontroluje, takže v jazyce C je bez problémů přeložitelná tato definice:
typedef  enum { CERVENA, MODRA=5, ZELENA, BILA=3, CERNA, SEDA } TBarvy;
Zde ale mají symbolické hodnoty MODRA a SEDA přiřazeny interní reprezentaci číslem 5, nejsou tedy v kódu rozlišitelné!

Poznámka: V jazycích se silnou typovou kontrolou (např. Pascal) je dovolenou používat v kódu pouze symbolická jména hodnot výčtového typu. Jazyk C je jazyk se slabou typovou kontrolou, kdy se provádějí kontroly pouze na úrovni vnitřní reprezentace. Je tedy možné zapsat do kódu přiřazení barva_auta = 2; Používat tento způsob se zásadně nedoporučuje, protože snižuje přehlednost a čitelnost kódu!


  [Obsah] [Cvičení 2]