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:
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ů.
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. |
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. |
Podrobný popis viz manuálová stránka k funkci printf()
Příklad:
- /*------------------------------------------------*/
- /* c07/print.c */
- #include <stdio.h>
- {
- 10.0, 10.0);
- }
- /*------------------------------------------------*/
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:
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:
- /*------------------------------------------------*/
- /* c07/print2.c */
- #include <stdio.h>
- {
- 200, 10.0, 10.0, 10.0);
- 10.0 / 3);
- }
- /*------------------------------------------------*/
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:
- | 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:
- /*------------------------------------------------*/
- /* c07/print3.c */
- #include <stdio.h>
- {
- x = -25l; /* je mozno psat jen x = -25, protoze
- prekladac vi, ze x je long a tak si
- cislo -25 prevede na -25l;
- takhle je to vsak "cistci" a citelnejsi */
- }
- /*------------------------------------------------*/
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()
.
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 :-).
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:
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 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:
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:
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:
- /*------------------------------------------------*/
- /* c07/scan1.c */
- #include <stdio.h>
- #define _CRT_SECURE_NO_WARNINGS
- {
- "napr. 10 0x0a nebo 012: ");
- }
- /*------------------------------------------------*/
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.
- /*------------------------------------------------*/
- /* c07/scan2.c */
- #define _CRT_SECURE_NO_WARNINGS
- #include <stdio.h>
- {
- }
- /*------------------------------------------------*/
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ě.
- /*------------------------------------------------*/
- /* c07/scan3.c */
- #define _CRT_SECURE_NO_WARNINGS
- #include <stdio.h>
- {
- int navrat;
- "hodnotu\n a stisknete ENTER. Napriklad: heslo 25\n>> ");
- /* heslo je 1234567890 */
- }
- /*------------------------------------------------*/
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.