Standardní vstup a výstup

Nyní na chvíli odbočím od výkladu syntaxe jazyka C a vysvětlím dvě funkce: printf() a její sestřičku scanf().

Funkci printf() používám již od prvního programu (a budu ji používat až do posledního) a tak se sluší řádně popsat její možnosti. V souvislosti s těmito funkcemi také objasním pojmy standardní vstup a výstup. Jen připomínám, že s funkcí printf(), resp. s řetězci, které tato funkce tiskne, úzce souvisí tzv. escape sekvence probírané v minulé kapitole.

Standardní vstup a výstup

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 taky monitor). Standardní vstup se označuje jako stdin, standardní výstup jako stdout a chybový výstup je stderr.

Vstup se řekne anglicky input a výstup output. Z toho vznikla často používáná zkratka I/O.

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áte 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).

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()

Funkce printf() se používá pro formátovaný standardní výstup (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í jsem již psal v souvislosti s funkcí main(). Ve stejné kapitole jsem mluvil také o funkci printf(). Podrobně je proberu 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> (obvykle má hodnotu -1) a k jeho využití se ještě vrátím.

Prvním argumentem funkce printf() je konstantní řetězec, který funkce tiskne do stdout. Ten může obsahovat speciální sekvence (začínají znakem %), na jejichž místo dosazuje funkce další argumenty. 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íte předat. Argumenty by měli mít očekávaný datový typ, který je dán speciální sekvencí.

Formát speciální sekvence prvního argumentu funkce printf() je následující:

Vše, co je v deklaraci v hranatých závorkách [ ] v deklaraci může, ale nemusí být. Svislítko | 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í (protože je to celé v hranatých závorkách).

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). Jsou to jediné dvě povinné části. Například už znáte 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ů.

Přehled typů speciální sekvence řetězce ve funkci printf()
typevý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.
p Ukazatel (pointer)
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. /* c07/print.c                                    */
  3.  
  4. #include <stdio.h>
  5.  
  6. int main(void)
  7. {
  8.     const char *COPYRIGHT = "(C)";
  9.     const int ROK = 2003;
  10.  
  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. Funkce printf() nemá tucha, jaký datový typ má její druhý argument, ona jen ví, na které adrese argument začíná. A podle %u hádá, že se jedná o int beze znaménka.

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:

Hodnoty width
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:

Hodnoty .prec
.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. /* c07/print2.c                                   */
  3.  
  4. #include <stdio.h>
  5.  
  6. int main(void)
  7. {
  8.     printf("%06i %06u %06x %6x %06f %06E %06G\n\n", -5, -5, 200,
  9.            200, 10.0, 10.0, 10.0);
  10.  
  11.     printf("%*s %1s %6s %%06.2G\n", 10, "%*i", "%06.2f", "%06.0E");
  12.  
  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. }
  18.  
  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 (viz „Posledni vystup“).
Na řádku 15 také vidíte, že do délky čísla (10) se započítává i desetinná tečka a délka desetinné části (4).

Jen pro jistotu ještě dovysvětlím začátek řádeku 11: printf("%*s …. Hvězdička znamená „délku vezmi z argumentu funkce“. Takže délka tisknutého řetězce je 10 (druhý argument funkce printf()). Přičemž tisknutý řetězec je další argument, tj. "%*i". Ve výstupu si můžete všimnout, že je řetězec "%*i" odsazený sedmi mezerami.

Příznak flags může nabývat hodnot z následující tabulky:

Hodnoty flags
- 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. /* c07/print3.c                                   */
  3.  
  4. #include <stdio.h>
  5.  
  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 */
  14.  
  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. }
  19.  
  20. /*------------------------------------------------*/

Výstup z programu:

    Cisla: <  +25> <  -25> <ffffffe7>
Cisla:     <+25  > < 25.000000> <0xffffffe7>

Funkce fflush()

Pozor! Pokud funkce printf() nevytikskne na konci znak nového řádku \n, může se stát, že výstup zůstane viset v tzv. vyrovnávací paměti a na obrazovku se nic nevypíše.

Vyrovnávací paměť (buffer) se vypíše, když funkce printf() vypíše znak nového řádku (\n), nebo když je buffer přeplňen, nebo když zavoláte funkci fflush().

int fflush(FILE *stream);

O datovém typu FILE budu psát až v souvislosti s prací se soubory. Teď vám bude stačit vědět, že když uvedete jako argument stdout, vyprázdní se buffer standardního výstupního souboru, což je to, co potřebujete :-).

#include <stdio.h>

int main(void) {
    printf("Rekni neco: ");
    fflush(stdout);
    getchar();
    return 0;
}

PS: to s tím bufferem nemusí fungovat vždy, záleží systém od systému, nebo jaký má váš OS zrovna náladu. Ale abyste měli jistotu, že váš program bude fungovat vždy, měli byste fflush() volat vždy, když hrozí, že se nevypíše ve správný okamžik to, co se vypsat má.

Funkce scanf()

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í:

Význam položek sekvence pro funkci scanf()
* 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:

význam type
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 vy místo toho na vstupu zadáte písmeno, pak dojde k chybě.
Pokud se načte 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:

int x;
scanf("%i", &x);

Takto se pokusí funkce scanf() načíst číslo a uložit jej do proměnné x, jejíž adresa je druhý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. Předchozí ukázka tak může vypadat i takto:

int x;
int *ui = &x;  /* ui je datoveho typu 'ukazatel na int' */
scanf("%i", ui);

Jestli začínáte mít zmatek ve všech těch hvězdičkách, ampersandech a procentech, dejte si kafe.

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áte nějaké znaky, nebo vstup ukončíte), vrátí se EOF. Návratovou hodnotu vás naučím využívat až v kapitolách věnovaných řízení programu.

Příklad:

  1. /*------------------------------------------------*/
  2. /* c07/scan1.c                                    */
  3.  
  4. #include <stdio.h>
  5. #define _CRT_SECURE_NO_WARNINGS
  6. int main(void)
  7. {
  8.     int x = -1;
  9.  
  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. }
  16.  
  17. /*------------------------------------------------*/
Visual Studio

Makro _CRT_SECURE_NO_WARNINGS umožňuje použít funkci scanf() ve VS. VS ji bez tohoto makra považuje za nebezpečnou a nedovolí ji použít. Místo ní vám nabízí funkci scanf_s() (s jako secure), což je bezpečná alternativa k scanf(). Funkce scanf_s() není součástí standardu jazyka C, proto ji nedoporučuji používat, pokud nechcete psát pouze a výhradně pro Visual Studio.

Nebezpečnost funkce scanf() tkví v tom, že když její argumenty neodpovídají očekávaným argumentům dle formátovacího řetězce, může funkce scanf() zapsat data do špatné části paměti, což může vést, mimo jiné, ke spuštění záškodnického kódu.

Microsoft zakazuje i další nebezpečné funkce a nabízí k nim alternativu s příponou _s. Obecně lze říci, že se jedná o všechny funkce, které čtou nebo zapisují někam do paměti, ale nemají parametr, který by omezoval délku čtených / zapisovaných dat.

Makro _CRT_SECURE_NO_WARNINGS musí být uvedené před #include <stdio.h>, resp. před každou knihovnou, která deklaruje „nebezpečnou“ funkci.

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 Tento text už se neneačte
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, 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í speciálních 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řív budu muset vysvětlit práci s poli.

  1. /*------------------------------------------------*/
  2. /* c07/scan2.c                                    */
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <stdio.h>
  5.  
  6. int main(void)
  7. {
  8.     int x = 0;
  9.     double f = 0.0;
  10.     char znak = 'a';
  11.  
  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);
  15.  
  16.     printf("Zadej retezec a pak racionalni cislo: ");
  17.     scanf("%*s %lf", &f);
  18.     printf("\t--- zadal jsi %f\n", f);
  19.  
  20.     return 0;
  21. }
  22.  
  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

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 spustilo 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 15.3 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. Snad jen, že je formátovací řetězec čitelnější.

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

Při druhém spuštění programu jsem poslal na vstup rovnou znak, číslo, řetězec, další číslo a další řetězec a odentroval jsem to. Program při prvním volání scanf() načetl B a 55, spustil printf() a další volání scanf() pokračovalo ve čtení vstupu tam, kde předchozí scanf() skončilo.
Přeskočilo řetězec „ahoj“ a načetlo číslo 15.3.

Mohli byste také zadat znak, stiskem ENTER výstup poslat programu, poté nějaké číslo a zase ENTER, řetězec a ENTER … 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žen jako čí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 (nemluvím teď o bílých znacích), 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. /* c07/scan3.c                                    */
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <stdio.h>
  5.  
  6. int main(void)
  7. {
  8.     int zamek = 5;
  9.     int navrat;
  10.  
  11.     printf("Hodnota zamku je nyni %+5i\n", zamek);
  12.     printf("Pokud chcete hodnotu zmenit, zadejte heslo a novou"
  13.            "hodnotu\n a stisknete ENTER. Napriklad: heslo 25\n>> ");
  14.  
  15.     /* heslo je 1234567890 */
  16.     navrat = scanf("1234567890 %i", &zamek);
  17.  
  18.     printf("Bylo nacteno %i spravnych polozek\n", navrat);
  19.     printf("Hodnota zamku je nyni %+5i\n", zamek);
  20.     return 0;
  21. }
  22.  
  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. Byla načtena jedna správná položka, tj číslo do proměnné zamek. Řetězec 1234567890 se sice taky správně přečetl, ale nikam neuložil, takže se nepočítá.

Hodnota zamku je nyni    +5
Pokud chcete hodnotu zmenit, zadejte heslo a novou hodnotu
a stisknete ENTER. Napriklad: heslo 25
>> heslo 25
Bylo nacteno 0 spravnych polozek
Hodnota zamku je nyni    +5

Načítání řetězců

Načítat řetězce znak po znaku není úplně efektivní. Než vám ale ukáži, jak načítat celé řetězce (pomocí sekvence %s), musím vám vysvětlit, co to vlastně řetězce jsou (jak jsou reprezentovány v paměti). Takže příklad na načítání řetězců je až v kapitole Pole a ukazatele.

Komentář Hlášení chyby
Created: 29.8.2003
Last updated: 18.10.2014
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..