[Cvičení 2] [Obsah] [Cvičení 4]

Cvičení 3


Témata


Načtení znaku z klávesnice a výpis znaku na obrazovku

Pro načtení jednoho znaku ze standarního vstupu (klávesnice, terminál) slouží funkce int getchar(), pro výstup jednoho znaku na standardní výstup (obrazovku, terminál) slouží funkce int putchar(int c) z knihovny stdio.h. Tzv. hlavičku funkce, tj. jak funkce vypadá, čteme následovně: funkce getchar vrací číslo typu int a nemá žádné parametry (prázdné závorky, upřesnění a detaily si povíme později). Funkce putchar vrací číslo typu int a má jeden parametr typu int. Funkce getchar() nevrací překvapivě hodnotu typu char, ale hodnotu typu int. Původ této skutečnosti je ve funkcích sloužící ke čtení znaků ze souboru, resp. k zápisu znaků do souboru. Návratové hodnoty mimo rozsah 0 až 255 (tedy rozsah odpovídající znakům) dovolují programu signalizovat, že došlo k nějaké chybě (např. konec souboru atd.). Ve funkci putchar(int c) je c znak vystupující na terminál. Samozřejmě, skutečným parametrem funkce putchar(int c) může být proměnná typu char, dojde k automatické konverzi. V případě, že návratová hodnotá funkce getchar() je přiřazena do proměnné typu char, překladač většinou hlásí varování typu „Conversion may lose significant digits“.

Příklad:

putchar('A'); putchar(65);

Úloha 3.1

Napište program, který přečte ze standardního vstupu (klávesnice) posloupnost anglických znaků zakončených klávesou Enter. Všechna velká písmena převede na malá a text vytiskne. Jednotlivé znaky čtěte pomocí funkce getchar(), na obrazovku je vypisujte pomocí putchar().
(Návod: použijte cyklus while((c=getchar())!='\n') { ... }, pro převod na malá písmena využijte operace sčítání a odčítání.)

Řešení:

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

Úloha 3.2

Upravte program pro výpočet obsahu obdelníka z minulého cvičení tak, aby se při chybném zadání stran program cyklicky dožadoval opětovného zadání.
Poznámka: Při chybném zadání zůstanou ve vyrovnávací paměti (bufferu) standardního vstupu (klávesnice) nepřečtené znaky. Funkce scanf při dalším průchodu tyto znaky opět interpretuje jako chybné zadání a cyklus čtení probíhá nekonečně dlouho. Proto je po chybném zadání nutné přečíst zbylé znaky ve vyrovnávací paměti. Protože je poslední znak Enter ('\n'), vyprázdníme vyrovnávací paměť tímto cyklem: while(getchar()!='\n');. Středník má zde význam prázdného příkazu.

Řešení:

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

Podmíněný výraz

Podmíněný výraz je ternární operátor, jehož operandy jsou tři výrazy. Je další ukázkou toho, jak lze psát v jazyce C efektivní a úsporný kód. Syntaxe podmíněného výrazu je následující:

výraz1 ? výraz2 : výraz3

Hodnota podmíněného výrazu je výraz2, pokud má výraz1 nenulovou hodnotu (neboli je pravdivý, true); v opačném případě má hodnotu výraz3.

Příklady:

(a==3) ? 1 : 0 // je-li hodnota proměnné a rovna 3, podmíněný výraz má hodnotu 1, jinak 0

Podmíněný výraz lze s výhodou využít např. při přiřazení:

x = (a==3) ? 1 : 0 // je-li hodnota proměnné a rovna 3, do proměnné x se přiřadí hodnota 1

Pomocí podmínky bychom zapsali přiřazení takto: if (a==3) x = 1; else x = 0;

Poznámka: První výraz není nutné uzavírat do závorky, ale často se uzavírá kvůli čitelnosti kódu. Uvědomte se, že podmíněným výrazem nelze nahradit jakoukoliv podmínku if !

Úloha 3.3

Přepište úlohu 3.1 za použití podmíněného výrazu.

Řešení:

Dev C++:malapis2.dev, malapis2.c
CodeBlocks:malapis2.cbp, malapis2.c
Dev C++:malapis3.dev, malapis3.c
CodeBlocks:malapis3.cbp, malapis3.c

Úloha 3.4

Napište program, který vytiskne absolutní hodnotu zadaného celého čísla (z klávesnice). Vyžijte podmíněného výrazu.

Řešení:

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

Operátor „ , “ (čárka)

Operátor čárka představuje jednu ze specialit jazyka C. Oddělují se jím jednotlivé výrazy ve výrazu (dovoluje napsat jakýsi vícenásobný výraz) a použití může nalézt tam, kde syntaxe jazyka dovoluje zapsat pouze jeden výraz. Tedy:

výraz1, výraz2, výraz3

Zapsaná trojice výrazů je považována za jeden výraz. Význam je následující: nejprve se vyhodnotí výraz1, pak výraz2 a naposledy výraz3. Hodnotou celého výrazu je hodnota posledního vyhodnoceného výrazu. Vše vysvětlí malý příklad, i když poněkud nesmyslný:

Příklad:

x = a=1, 3, a+b;

Nejprve se vyhodnotí přiřazovací výraz, tedy do proměnné a se přiřadí hodnota 1; hodnota výrazu a=1 je 1, ale tato hodnota se „zahodí“. Vyhodnotí se další výraz, tedy 3, tato hodnota se opět „zapomene“. Nakonec se sečtou hodnoty proměnných a a b. Součet je hodnotou celého výrazu a je přiřazen do proměnné x.

Z příkladu je viditelné, že operátor čárka není příliš přehledný z hlediska čitelnosti kódu, čtenář možná pochybuje o jeho smyslu (a v tomto případě má pravdu). Proto je také doporučeno omezit jeho používání pouze v případě cyklu typu for. Nicméně si ukážeme smysluplnější příklad s operátorem čárky mimo cyklus for.

Úloha 3.5

Napište program, který do proměnné x načte z klávesnice celé číslo. Pokud je číslo záporné, vytiskne na obrazovku upozornění a do proměnné y přiřadí absolutní hodnotu čísla. Použijte podmíněný výraz a operátor čárky a funkci printf.

Řešení:

Uvědomíme se, že printf je funkce a že ji lze použít ve výrazu. Řešení může vypadat např. následovně:

int main(int argc, char **argv)
{
  int x,y;
  scanf("%d",&x);
  y = (x<0) ? printf("Zadal jste zaporne cislo"),-x : x;
  system("PAUSE");
  return 0;
}
Pokud je hodnota proměnné x < 0, vyhodnocuje se výraz printf("Zadal jste zaporne cislo"),-x.Ten je tvořen dvěma výrazy oddělenými operátorem čárka - nejprve se vyhodnotí funkce printf, tj. vytiskne se upozornění a funkce printf vrátí počet vytištěných znaků. Tato hodnota se „zahodí“ a vyhodnotí se další výraz -x. To je konečný výsledek, který se přiřadí do proměnné y.
Dev C++:cislo.dev, cislo.c
CodeBlocks:cislo.cbp, cislo.c

Cyklus for

Cyklus for má v jazyku C poněkud odlišnou syntaxi i sémantiku než v jazycích typu Pascal nebo Basic. Jeho možnosti jsou mnohem bohatší oproti zmíněným jazykům. Obecný zápis cyklu vypadá takto:

for(výraz1;výraz2;výraz3) příkaz;

Chování cyklu for je ekvivaletní cyklu typu while s tímto zápisem:

výraz1;
while(výraz2)
{
  příkaz;
  výraz3;
}
Nejprve se vyhodnotí výraz1 (pouze jednou, před vstupem do cyklu!). Pak se provádí tělo cyklu, dokud je splněn výraz2. Na konci každého provedení těla cyklu je vždy ještě vyhodnocen výraz3. Typické využití cyklu for je ve tvaru cyklu s pevným počtem opakování, např.

for(i=1;i<=n;i++) příkaz;, resp. for(i=n;i>=1;i--) příkaz;

Na místě výrazů mohou být skutečně libovolné výrazy, tedy na místě výraz2 může být libovolně složitá podmínka, zvětšit hodnotu proměnné na konci cyklu je možné např. o 2 aj.

Poznámka: Proměnná i se nazývá řídicí proměnná cyklu.
Ekvivalence obou cyklů je ukázána na jednoduchém příkladě, který vypíše násobilku 3, viz tabulka 1.

Cyklus forCyklus while
int i;

for(i=1;i<=10;i++)
  printf("%d",3*i);
int i;

i=1;
while(i<=10)
{
  printf("%d",3*i);
  i++;
}  

Tabulka 1: Ekvivalence cyklů

Pro ověření, zda dobře rozumíte cyklu for, zkuste analyzovat následující příklad.

Úloha 3.6

Napište program, který vypočte součet přirozených čísel od 1 do n, číslo n se zadává z klávesnice.

Řešení:
Na této úloze si ukážeme několik specialit, které cyklus for v jazyce C nabízí. V popisech řešení vynecháme pro stručnost deklaraci proměnných (všechny jsou typu int), načtení čísla n z klávesnice a výpis výsledku na obrazovku.
Přirozené řešení, odpovídající filozofii zápisu algoritmu i v jiných jazycích, uvedeme jako první:

s = 0;
for(i=1;i<=n;i++) s= s + i;
resp.
s = 0;
for(i=1;i<=n;i++) s+=i;
Právě zde je rozumné využít operátor čárka a přidat inicializační výraz s = 0 do cyklu k inicializaci proměnné i:

for(s=0,i=1;i<=n;i++) s+=i;
Libovolný z výrazů je možné v cyklu vynechat. Provedeme-li počáteční inicializaci před cyklem, vynecháme první výraz:

s=0;
i=1;
for(;i<=n;i++) s+=i;

Pokud bychom chtěli zvýšení hodnoty proměnné i o jedničku provést v těle cyklu současně se sumací (využijeme operátor ++ v postfixové formě), můžeme napsat:

for(s=0,i=1;i<=n;) s+=i++;

V krajním případě bychom mohli celý výraz představující sumaci a následné zvýšení hodnoty proměnné i zahrnout do cyklu jako výraz3 a tělo ponechat prázdné:

for(s=0,i=1;i<=n;s+=i++); /* zde ma strednik vyznam prazdneho prikazu v tele cyklu */
Poslední dvě řešení se nedoporučuje příliš využívat, protože slučují příkazy těla cyklu a kód související s řídicí proměnnou cyklu, což ubírá na čitelnosti a přehlednosti kódu. Nicméně příklady poslouží jako vhodná ukázka, jaká „kouzla“ jazyk C dovoluje.

Úloha 3.7

Napište program, který načte z klávesnice dvě přirozená čísla a a b. Zkontroluje, zda je a menší než b, pokud ne, hodnoty proměnných prohodí. Program vypíše všechna sudá čísla mezi a a b včetně těchto čísel, jsou-li samozřejmě sudá.
(Návod: Cyklus začněte provádět od hodnoty a, je-li sudá, nebo od hodnoty a+1, je-li lichá. Řídicí proměnnou cyklu zvyšujte o 2. Pro inicializaci řídicí proměnné cyklu použijte podmíněný výraz.)

Polotovar:

Dev C++:suda.dev, suda.c
CodeBlocks:suda.cbp, suda.c
Řešení:
Dev C++:suda.dev, suda.c
CodeBlocks:suda.cbp, suda.c

Úloha 3.8

Napište program, který vypočítá faktoriál přirozeného čísla n. Napište program iterativně s cyklem for, nikoliv s využitím rekurze.

Polotovar:

Dev C++:faktorial.dev, faktorial.c
CodeBlocks:faktorial.cbp, faktorial.c
Řešení:

Poznámka: S cykly (se všemi typy) v jazyce C souvisejí dvě klíčová slova: break a continue. Příkaz break ukončuje předčasně cyklus (používá se v souvislosti s nějakou podmínkou). Příkaz continue ukončí přeruší pouze aktuální iteraci, provede se skok na testovací podmínku, je-li splněna, provádí se další iterace.

Jako ukázku přepíšeme cyklus z úlohy 3.1 s využitím break a continue. Reálný kód by takto vypadat ale neměl.

while (1)
{
  c = getchar();
  if (c == '\n') break;
  if (c >= 'A' && c <= 'Z') { putchar(c+('a'-'A'));  continue; }
  putchar(c);
}

Příkaz switch

Příkaz switch představuje přepínač variant, je obdobou příkazu case v jazyce Pascal. Obecný tvar příkazu můžeme zapsat:
switch (výraz)
{
  case konstanta1: příkaz1;
  case konstanta2: příkaz2;
  atd.
  default: příkaz;
}
Výraz je libovolný výraz, který musí být celočíselného typu (nelze tedy použít přepínač např. s proměnnými typu float nebo double). Jednotlivé varianty, zapsané za klíčovým slovem case, mohou být pouze konstanty, není dovoleno psát proměnné ani např. intervaly ohraničené konstantami. Pokud je nutné zapsat k dané variantě více příkazů, nemusejí být tentokrát uzavřeny do bloku mezi znaky { ... }. Varianta uvedená za klíčovým slovem default je vykonána v případě, že hodnota výrazu neodpovídá ani jedné z uvedených variant. Varianta default je nepovinná, pokud není uvedena a hodnota výrazu neodpovídá žádné z variant, žádný příkaz se neprovede.

Příklad:

switch(getchar())
{
  case '1': printf("Zadal jste 1!");
  case '2': printf("Zadal jste 2!");
  case '3': printf("Zadal jste 3!");
}
Je pravděpodobné, že skutečné chování příkazu (jak jsme jej zapsali výše) se bude lišit od čtenářovy představy. Zadá-li uživatel programu znak 2, na obrazovce se vytiskne následující text:
Zadal jste 2!
Zadal jste 3!
Zadaný znak se nejprve porovná s konstantou '1', není-li shodný, pokračuje se v porovnání s konstantou '2' atd. Nastane-li shoda, provedou se příkazy u dané varianty a pokračuje se v provádění příkazů u dalších variant (příkaz switch je většinou přeložen do strojového kódu jako „rozskok“ pomocí tabulky a instrukce typu JMP). Abychom dosáhli výběru jediné varianty, je třeba doplnit jednotlivé větve klíčovým slovem break:
switch(getchar())
{
  case '1': printf("Zadal jste 1!"); break;
  case '2': printf("Zadal jste 2!"); break;
  case '3': printf("Zadal jste 3!"); break;
  default: printf("Zadal jste něco jiného");
}
Pak bude po zadání znaku 2 na obrazovce skutečně jedna věta:
Zadal jste 2!
Na uvedení příkazu break programátoři, kteří začínají s jazykem C, často zapomínají.

Vynechání příkazu break může být účelem, je dovoleno vynechat dokonce i příkaz. Tímto způsobem se zajistí obsloužení více variant jedním kódem:

switch(getchar())
{
  case '1': printf("Zadal jste 1!"); break;
  case '2':
  case '3': printf("Zadal jste 2 nebo 3!"); break;
  default: printf("Zadal jste něco jiného");
}
Upozornění: S konstrukcí switch souvisí pouze příkaz break, nikoliv continue.

Úloha 3.9

Napište program, který počítá obsah a obvod obdélníka. Na obrazovku vypíše jednoduché menu o čtyřech položkách
1 ... Zadání stran
2 ... Obsah
3 ... Obvod
4 ... Konec
Velikosti stran uchovávejte po zadání ve dvou proměnných. Číslo varianty načtěte do proměnné typu int pomocí funkce scanf, výpočet větvete pomocí příkazu switch.

Polotovar:

Dev C++:obdelnik.dev, obdelnik.c
CodeBlocks:obdelnik.cbp, obdelnik.c
Řešení:


[Cvičení 2] [Obsah] [Cvičení 4]