| ← klíčová slova, konstanty, řetězce | C/C++ | práce s typy dat → |
Nyní na chvíli odbočíme od výkladu syntaxe jazyka C a podíváme se na dvě funkce: printf() a její sestřičku scanf().
Funkci printf() používáme již od prvního programu (a budeme používat i nadále) a tak se sluší řádně popsat její možnosti. V souvislosti s těmito funkcemi si také objasníme pojmy standardní vstup a výstup. Jen připomínám, že s funkcí printf() úzce souvisí tzv. escape sekvence probírané v minulé kapitole.
Každý program při svém spuštění dostane od operačního systému standardní vstup (obvykle klávesnice), standardní výstup (obvykle monitor) a standardní chybový výstup (obvykle monitor). Standardní vstup se označuje jako stdin, standardní výstup jako stdout a chybový výstup je stderr.
Funkce, které s těmito vstupy a výstupy pracují jsou definované v hlavičkovém souboru <stdio.h>. Například funkce printf() tiskne vše do standardního výstupu stdout. V OS máme možnost standardní vstupy a výstupy přesměrovat (například výstup do souboru místo na obrazovku). Pokud používáte Linux, jistě vám je známé přesměrování standardního výstupu pomocí znaku > a chybového výstupu pomocí znaků 2>. Pokud je vstup nebo výstup přesměrován, program to de facto ani nezpozoruje (ne že by to nešlo zjistit) a tak se tím nemusíte nijak zatěžovat.
Vstupní a výstupní zařízení lze rozdělit na znaková a bloková. Znaková jsou například klávesnice a monitor, bloková např. pevné disky. Rozdíl je především v možnosti získávání či vkládání dat z/do zařízení. Ke znakovým zařízením se přistupuje sekvenčně tedy znak po znaku a k blokovým tzv. náhodným přístupem (random access). Znamená to, že z blokových zařízení můžete přistupovat k datům dle libosti (můžete číst z kterékoliv části disku, nebo do kterékoliv části zapisovat), kdežto u sekvenčních zařízení lze číst nebo zapisovat pouze po sekvencích (např. na monitor nebo tiskárnu lze zapisovat pouze řádek za řádkem (alespoň dřív to nebylo možné jinak) a z klávesnice můžete číst jenom ty znaky, které uživatel právě stiskl (ty, které stiskl před minutou nebo stiskne až za minutu nezjistíte)).
Funkce printf() se používá pro formátovaný standardní výstup (do stdout). Deklarace funkce printf() vypadá takto:
int printf (const char *format [, arg, ...]);
Něco málo o argumentech a návratových hodnotách funkcí jsme již říkali v souvislosti s funkcí main(). Ve stejné kapitole jsem mluvil také o funkci printf(). Podrobně je probereme až v kapitole věnované vytváření funkcí.
Funkce printf() má návratovou hodnotu typu
int. Tato hodnota je rovna počtu znaků zapsaných do výstupu, nebo
speciální hodnota EOF
v případě chyby zápisu. Hodnota EOF se používá jako označení konce souboru
(End Of File), taktéž konce standardního vstupu a výstupu. EOF je
definován v souboru <stdio.h>
a k jeho využití se ještě vrátíme.
Prvním argumentem je konstantní řetězec, který funkce printf() tiskne do stdout.
Ten může obsahovat speciální sekvence, na jejichž místo
dosazuje funkce další argumenty (začínají znakem %).
Z toho vyplývá, že kolik je
speciálních sekvencí v tomto řetězci, tolik dalších argumentů
oddělených čárkou funkci printf() musíme předat.
Argumenty by měli mýt očekávaný datový typ, který je dán speciální sekvencí.
Formát této sekvence je následující:
%[flags][width][.prec][h|l|L]type
Vše, co je v deklaraci v hranatých závorkách [ ] v deklaraci může, ale nemusí být. Znak | chápejte jako „nebo“. Například [h|l|L] znamená, že v deklaraci sekvence může být h nebo l nebo L nebo ani jedna z těchto možností.
Jak vidíte, sekvence začíná znakem procento a končí znakem určující typ argumentu, který se bude tisknout (a také jak (!) se bude tisknout). Například už známe sekvenci %i která tiskne celé číslo se znaménkem. Pokud chcete vytisknout přímo znak procento, pak jej stačí napsat dvakrát za sebou (%%). V následující tabulce je přehled dalších možných typů.
| type | význam |
|---|---|
| d, i | Celé číslo se znaménkem (Zde není mezi d a i rozdíl. Rozdíl viz scanf() níže). |
| u | Celé číslo bez znaménka. |
| o | Číslo v osmičkové soustavě. |
| x, X | Číslo v šestnáctkové soustavě. Písmena ABCDEF se budou tisknout jako malá při použití malého x, nebo velká při použití velkého X. |
| f | Racionální číslo (float, double) bez exponentu. |
| e, E | Racionální číslo s exponentem, implicitně jedna pozice před desetinnou tečkou a šest za ní. Exponent uvozuje malé nebo velké E. |
| g, G | Racionální číslo s exponentem nebo bez něj (podle absolutní hodnoty čísla). Neobsahuje desetinnou tečku, pokud nemá desetinnou část. |
| c | Jeden znak. |
| s | Řetězec. |
Příklad:
1: /*------------------------------------------------*/ 2: /* print.c */ 4: #include <stdio.h> 6: int main(void) 7: { 8: const char *COPYRIGHT = "(C)"; 9: const int ROK = 2003; 11: printf("%i %u %o %x %X %f %e %G\n", -5, -5, 200, 200, 200, 10.0, 12: 10.0, 10.0); 13: printf("%s %i\n", COPYRIGHT, ROK); 14: return 0; 15: } 16: /*------------------------------------------------*/
Výstup bude následující:
-5 4294967291 310 c8 C8 10.000000 1.000000e+01 10 (C) 2003
Z čísla -5 se stalo při použití %u číslo 4294967291. To je dáno interpretací bitového zápisu, kdy se první bit zleva nepočítal jako identifikátor znaménka, ale jako součást čísla.
Položky width a .prec určují délku výstupu. Délka width určuje minimální počet znaků na výstupu. Mohou být uvozeny mezerami nebo nulami. .prec určuje maximální počet znaků na výstupu pro řetězce. Pro celá čísla je to minimum zobrazených znaků, pro racionální počet míst za desetinnou tečkou (má to tedy více významů v závislosti na typu).
Položka width může nabývat následujících hodnot:
| n | Vytiskne se nejméně n znaků doplněných mezerami |
|---|---|
| 0n | Vytiskne se nejméně n znaků doplněných nulami |
| * | Vytiskne se nejméně n znaků, kde n je další argument funkce printf() |
Položka .prec může nabývat následujících hodnot:
| .0 | pro e, E, f nezobrazí desetinnou tečku |
|---|---|
| pro d, i, o, u, x nastaví standardní hodnoty | |
| .n | pro d, i, o, u, x minimální počet číslic |
| pro e, E, f počet desetinných číslic | |
| pro g, G počet platných míst | |
| pro s maximální počet znaků | |
| .* | jako přesnost bude použit následující parametr funkce printf() |
Příklad:
1: /*------------------------------------------------*/ 2: /* print2.c */ 4: #include <stdio.h> 6: int main(void) 7: { 8: printf("%06i %06u %06x %6x %06f %06E %06G\n\n", -5, -5, 200, 200, 10.0, 9: 10.0, 10.0); 11: printf("%*s %1s %6s %%06.2G\n", 10, "%*i", "%06.2f", "%06.0E"); 13: printf("%*i %06.2f %06.0E %06.2G\n", 10, -5, 10.0 / 3, 10.0 / 3, 14: 10.0 / 3); 15: printf("\n%.8s %0*.*f\n", "Posledni vystup:", 10, 4, 10.0 / 3); 16: return 0; 17: } 19: /*------------------------------------------------*/
Výstup z programu:
-00005 4294967291 0000c8 c8 10.000000 1.000000E+01 000010
%*i %06.2f %06.0E %06.2G
-5 003.33 03E+00 0003.3
Posledni 00003.3333
Všimněte si použití %% při druhém volání funkce printf() (řádek 11) a také rozdílu mezi %06.2f a %06.2G (řádek 13). Také si všimněte, že do délky čísla se započítává i pozice pro znaménko. Čísla, která jsou delší než požadovaná minimální délka se nezkracují, řetězce ano.
Příznak flags může nabývat hodnot z následující tabulky:
| - | výsledek je zarovnán zleva |
|---|---|
| + | u čísla bude vždy zobrazeno znaménko |
| mezera | u kladných čísel bude místo znaménka "+" mezera |
| # | pro o, x, X výstup jako konstanty jazyka C |
| pro e, E, f, g, G vždy zobrazí desetinnou tečku | |
| pro g, G ponechá nevýznamné nuly | |
| pro c, d, i, s, u nemá význam. |
Znaky h l a L označují typ čísla. Znak h typ short (nemá smysl pro 16bitové překladače), l dlouhé celé číslo, L long double.
Příklad:
1: /*------------------------------------------------*/ 2: /* print3.c */ 4: #include <stdio.h> 6: int main(void) 7: { 8: long int x; 9: long double y = 25.0L; 10: x = -25l; /* je mozno psat jen x = -25, protoze 11: prekladac vi, ze x je long a tak si 12: cislo -25 prevede na -25l; 13: takhle je to vsak "cistci" a citelnejsi */ 15: printf("%10s <%+5i> <% 5ld> <%x>\n", "Cisla:", 25, x, -25); 16: printf("%-10s <%-+5i> <% 5Lf> <%#x>\n", "Cisla:", 25, y, -25); 17: return 0; 18: } 20: /*------------------------------------------------*/
Výstup z programu:
Cisla: < +25> < -25> <ffffffe7>
Cisla: <+25 > < 25.000000> <0xffffffe7>
Funkce scanf se používá pro formátovaný standardní vstup (z stdin), což bývá obvykle vstup z klávesnice. Deklarace funkce scanf() vypadá takto:
int scanf (const char *format [, address, ...]);
Návratová hodnota je rovna počtu bezchybně načtených a do paměti uložených položek, nebo hodnota EOF (End Of File) při pokusu číst položky z uzavřeného vstupu. První argument je stejně jako u funkce printf() řetězec, který obsahuje speciální sekvence určující, co se má načíst. Formát takové sekvence je následující:
%[*][width][h|l|L]type
Význam položek je následující:
| * | přeskočí popsaný vstup (načte, ale nikam nezapisuje) |
|---|---|
| width | maximální počet vstupních znaků |
| h|l|L | modifikace typu (jako u printf()) |
| type | typ konverze. |
Jak vidíte, sekvence vždy začíná znakem procento a končí typem konverze. Možné typy konverzí jsou v následující tabulce:
| d | celé číslo |
|---|---|
| u | celé číslo bez znaménka |
| o | osmičkové celé číslo |
| x | šestnáctkové celé číslo |
| i | celé číslo, zápis odpovídá zápisu konstanty v jazyce C, např. 0x uvozuje číslo v šestnáctkové soustavě |
| n | počet dosud přečtených znaků aktuálním voláním funkce scanf() |
| e, f, g | racionální čísla typu float, lze je modifikovat pomocí l a L |
| s | řetězec; úvodní oddělovače jsou přeskočeny, na konci je přidán znak '\0' |
| c | vstup znaku; je li určena šířka, je čten řetězec bez přeskočení oddělovačů |
| [search_set] | jako s, ale se specifikací vstupní množiny znaků, je možný i interval, například %[0-9], i negace, například %[^a-c]. |
Oddělovače jsou tzv. bílé znaky (tabulátor, mezera, konec řádku (ENTER)). Ty se při čtení ze vstupu přeskakují (výjimkou může být typ c). Načítání tedy probíhá tak, že se nejdříve přeskočí oddělovače a poté se načte požadovaný typ. Pokud je požadovaný typ například číslo, ale mi místo toho na vstupu máme písmeno, pak dojde k chybě. Pokud načteme požadovaný typ, uloží se na adresu, která je uložena v dalším argumentu funkce scanf(). Volání funkce scanf() může vypadat např. takto:
scanf("%i",&x);
Takto se pokusí funkce scanf() načíst číslo a uložit jej do proměnné x, jejíž adresa je dalším argumentem funkce scanf(). Znovu zdůrazňuji, že je to adresa místa v paměti, kde je proměnná x, do které se načtená hodnota uloží. Adresa proměnné se získává pomocí operátoru & a může být uložena v ukazateli.
Pokud se to podaří, vrátí funkce číslo 1 (načtena jedna správná položka). Pokud se to nepodaří (např. místo čísla zadáme nějaké znaky, nebo vstup ukončíme), vrátí se EOF. Návratovou hodnotu se naučíme využívat až v kapitolách věnovaných řízení programu.
Příklad:
1: /*------------------------------------------------*/ 2: /* scan1.c */ 4: #include <stdio.h> 6: int main(void) 7: { 8: int x = -1; 10: printf("Zadej cislo jako konstantu jazyka C\n" 11: "napr. 10 0x0a nebo 012: "); 12: scanf("%i", &x); 13: printf("Zadal jsi cislo %i\n", x); 14: return 0; 15: } 17: /*------------------------------------------------*/
Když program přeložíte a spustíte, bude funkce scanf() čekat na vstup (z klávesnice), dokud nějaký nedostane, nebo dokud nebude vstup uzavřen. Vstup z klávesnice se programu odešle až po stisku klávesy ENTER.
Možné výstupy:
Zadej cislo jako konstantu jazyka C napr. 10 nebo 0x0a nebo 012: -50 Zadal jsi cislo -50
Zadej cislo jako konstantu jazyka C napr. 10 nebo 0x0a nebo 012: 0xff Zadal jsi cislo 255
Zadej cislo jako konstantu jazyka C napr. 10 nebo 0x0a nebo 012: ff Zadal jsi cislo -1
Nyní již umíte vytvářet skutečně interaktivní programy (-:
Při posledním spuštění programu jsem zadal „ff“, což není číslo ale textový řetězec, proto funkce scanf() do proměnné x nic neuložila a tak v ní zůstalo číslo -1.
Vstupní proud (stdin) můžete přerušit ve Windows a v DOSu klávesovou zkratkou CTRL+Z (stiskněte a držte CTRL a k tomu stiskněte písmeno "z"). V Linuxu pomocí klávesové zkratky CTRL+D. Vyzkoušejte.
Ukážu ještě jeden příklad a další si vymyslete sami. Kombinací sekvencí může být jak u funkce printf() tak scanf() mnoho, takže si s nimi vyhrajte a sledujte jak pracují. Zatím se nepokoušejte načítat řetězce, nejdříve si budeme muset vysvětlit práci s poli.
Řetězec ze standardního vstupu je ukončen oddělovačem (mezera, ENTER, ...).
1: /*------------------------------------------------*/ 2: /* scan2.c */ 4: #include <stdio.h> 6: int main(void) 7: { 8: int x = 0; 9: double f = 0.0; 10: char znak = 'a'; 12: printf("Zadej jeden znak a cislo max. 2 znaky dlouhe: "); 13: scanf("%c %2d", &znak, &x); 14: printf("\t--- zadal jsi %c a %i\n", znak, x); 16: printf("Zadej retezec a pak racionalni cislo: "); 17: scanf("%*s %lf", &f); 18: printf("\t--- zadal jsi %f\n", f); 20: return 0; 21: } 23: /*------------------------------------------------*/
Možné výstupy z programu:
Zadej jeden znak a cislo max. 2 znaky dlouhe: B 55
--- zadal jsi B a 55
Zadej retezec a pak racionalni cislo: ahoj 15.3
--- zadal jsi 15.300000
Zadej jeden znak a cislo max. 2 znaky dlouhe: B 55 ahoj 15.3 nazdar
--- zadal jsi B a 55
Zadej retezec a pak racionalni cislo: --- zadal jsi 15.300000
Všimněte si, jak probíhá načítání ze standardního vstupu. V prvním příkladě jsem po spuštění programu napsal „B 55“ a stiskl ENTER. Tím sem předal nějaký vstup a první volání funkce scanf() si načetlo nejdříve požadovaný znak a poté číslo. Pak proběhla funkce printf() a spustila se druhé volání scanf(). Ta se zastavila a čekala na další vstup. Napsal jsem tedy řetězec „ahoj“ a za ním racionální číslo a poslal tento vstup stiskem ENTERu. Funkce scanf() řetězec přeskočila (%*s) a načetla číslo za ním.
Mezery, které jsou ve formátovacím řetězci funkce scanf(), nehrají žádnou roli a nemusí tam být.
Při druhém spuštění programu jsem poslal na vstup rovnou znak,
číslo, řetězec, další číslo a další řetězec. Program při prvním
volání scanf()
načetl B a 55, spustil printf()
a další volání scanf()
pokračovalo v čtení vstupu tam, kde předchozí scanf() skončilo.
Přeskočilo řetězec „ahoj“ a načetlo číslo 15.3.
Mohli by jste také zadat znak, stiskem ENTER výstup poslat
programu poté číslo a zase ENTER, řetězec a ENTER
...atd. Zkoušejte si a sledujte jak se program chová.
Všimněte si, že když zadáte číslo, jež má být dlouhé jen 2 znaky, pak každý
další znak už druhé volání funkce scanf()
vyhodnotí jako řetězec.
Také si uvědomte, že číslo může být chápáno též jako řetězec nebo
znak. Jde jen o to, jak bude do paměti ukládáno. Pokud načítáte
číslo 5, bude uloženo jako číslo 5, pokud načítáte znak
5 bude uloženo číslo z ASCII
tabulky odpovídající znaku 5 (což je číslo 53).
Pokud se v prvním argumentu funkce scanf() vyskytne nějaký řetězec, znamená to, že funkce scanf() má právě tento řetězec načíst (a nikam neuložit). Pokud však takový řetězec na vstupu není, dojde k chybě.
1: /*------------------------------------------------*/ 2: /* scan3.c */ 4: #include <stdio.h> 6: int main(void) 7: { 8: int zamek = 5; 9: int navrat; 11: printf("Hodnota zamku je nyni %+5i\n", zamek); 12: printf("Pokud chcete hodnotu zmenit, zadejte heslo a novou hodnotu\n" 13: "a stisknete ENTER. Napriklad: heslo 25\n>> "); 15: /* heslo je 1234567890 */ 16: navrat = scanf("1234567890 %i", &zamek); 18: printf("Bylo nacteno %i spravnych polozek\n", navrat); 19: printf("Hodnota zamku je nyni %+5i\n", zamek); 20: return 0; 21: } 23: /*------------------------------------------------*/
Výstup z programu:
Hodnota zamku je nyni +5 Pokud chcete hodnotu zmenit, zadejte heslo a novou hodnotu a stisknete ENTER. Napriklad: heslo 25 >> 1234567890 -2345 Bylo nacteno 1 spravnych polozek Hodnota zamku je nyni -2345
Všimněte si jak byla vložena návratová hodnota funkce scanf() do proměnné navrat.
| ← klíčová slova, konstanty, řetězce | C/C++ | práce s typy dat → |
