{{indexmenu_n>2}}
====== 2 - Vstup a výstup programu ======
* [[courses:b0b36prp:internal:tutorialinstruction:02|pro vyučující]]
/*
Bude upřesněno před začátkem semestru.
*/
===== Cíle cvičení =====
- Kompilace a spuštění programu.
- Návratová hodnota programu a její zpracování v OS.
- První cyklus.
- Vstup a výstup programu[[http://www.tutorialspoint.com/c_standard_library/c_function_getchar.htm|getchar]] a standardní výstup - [[https://www.tutorialspoint.com/c_standard_library/c_function_putchar.htm|putchar]].
- Formátovaný vstup a výstup - [[http://www.tutorialspoint.com/c_standard_library/c_function_scanf.htm|scanf]],[[http://www.tutorialspoint.com/c_standard_library/c_function_printf.htm|printf]] a [[http://www.cplusplus.com/reference/cstdio/fprintf/|fprintf]].
- [[https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B|Operátory]] a základní datové typy - ''char, int, float, double''.
- Standardní vstup, výstup a chybový výstup a jejich přesměrování.
===== Úkoly =====
==== Kompilace a spouštění programu ====
* Vytvořte program "Hello PRP", který uložte do souboru program.c v domovském adresáři, např. ''~/prp/lab02''.
#include
int main(void)
{
printf("I like B0B36PRP!\n");
return 0;
}
* Program zkompilujte a spusťte. Zobrazte návratovou hodnotu, např. z interpreteru příkazů (shell) příkazem ''echo $?''.
{{:courses:b0b36prp:labs:vscode-lab01-return_value.png?600|}}
==== Návratová hodnota programu ====
* Vyzkoušejte změnit návratovou hodnotu a opět vypsat.
* Vytvořte dvě spustitelné verze programu např. ''program0'' a ''program1'', které budou mít návratovou hodnotu 0 a 1. Využijte přepínač kompilátoru -o, např. kompilací ''clang program.c -o program0'' a ''clang program.c -o program1''.
* Vyzkoušejte chování a spuštění programu
./program0 && ./program1
./program1 && ./program0
a
./program0 && echo $? && ./program1 && echo $?
./program1 && echo $? && ./program0 && echo $?
a dále pak
./program0 && echo $? && ./program1; echo $?
./program1; echo $? && ./program0 && echo $?
==== První cyklus ====
* Program rozšiřte o násobný výpis textové zprávy
#include
int main(void)
{
printf("I like B0B36PRP!\n");
printf("I like B0B36PRP!\n");
printf("I like B0B36PRP!\n");
printf("I like B0B36PRP!\n");
return 0;
}
který následně upravte využitím cyklu
#include
int main(void)
{
for (int i = 0; i < 4; ++i) {
printf("I like B0B36PRP!\n");
}
return 0;
}
Vyzkoušejte kompilaci a spuštění programu, např.
clang program.c && ./a.out
nebo
clang program.c -o program && ./program
* V programu udělejte chybu a zkuste podmíněné spuštění programu na základě kompilace.
* Program dále upravit s hlavičkou funkce ''main'' s předáním argumentů programu, např.
#include
int main(int argc, char *argv[])
{
for (int i = 0; i < argc; ++i) {
printf("I like B0B36PRP!\n");
}
return 0;
}
* Vyzkoušejte chování programu pro spuštění s různým počtem argumentů. Kolik řádku program vypíše pro případy
* ''./program''
* ''./program 1 2 3''
* ''./program a b c e''
* Počet řádků můžete spočítat nástrojem (příkazem) ''wc'' (viz ''man wc''), například ''./program 2 3 4 | wc''.
==== Vstup a výstup programu ====
* Předcházející příklad rozšiřte o načtení počtu opakování načtením jednoho znaku funkcí ''getchar()'', prostudujte si dokumentaci např. ''man getchar''.
int r = getchar();
for (int i; i < r; ++i) {
printf("I like B0B36PRP!\n");
}
Je uvedný program správně? Bude vždy fungovat podle očekávání?
* Vyzkoušejte chování programu pro vstup '0', //pro vstup je nutné odeslat znak (či více znaků) klávesou ''enter''//.
* Kolik vypíše program řádku pro vstup ''1'', nechte spočítat přesměrovaný výstup do programu ''wc'', např. ''./a.out | wc'' a konzultujte s [[https://en.wikipedia.org/wiki/ASCII| ASCII tabulku]].
* Program upravte převodem znaku na číslíci například ''r = r - '0';'' a zkuste znovu program pro hodnoty 1 až 9.
* Dále spusťte program s přesměrovaným vstupem, např. ''echo 1 | ./program''.
* Vytvořte soubor se vstupem např. ''echo 3 > in.txt'', který můžete zobrazit např. ''cat in.txt'' nebo otevřít v editoru ''gedit in.txt'' nebo přímo ve VS Code ''code in.txt''.
* Spusťte program s přesměrovaným vstupem ze souboru, např. ''./program < in.txt''. Edituje obsah souboru ''in.txt''.
* Vytvořte prázdný soubor příkazem ''touch empty.txt'' a vyzkoušejte chování program pro takový vstup.
* Program rozšiřte o výpočet návratové hodnoty programu ''return r < 0 || r > 9;''
#include
int main(void)
{
int r = getchar();
r = r - '0';
for (int i = 0; i < r; ++i) {
printf("I like B0B36PRP!\n");
}
return r < 0 || r > 9;
}
* Vyzkoušejte výstupy programu pro různé vstupy např.
./program
* Diskutujte chování programu a návratovou hodnotu programu. Jak se program chová při prázdném vstupu, jakou návratovou hodnotu má funkce ''getchar()''?
==== Formátovaný výstup a standardní chybový výstup ====
* Předchozí příklad rozšiřte vypsání počtu vypisovaných řádků funkcí ''printf'', např. ''printf("%d\n", r);'', viz ''man printf''.
* V tomto případě bude standardní výstup vždy vypisovat jeden řádek navíc, proto použití ''wc'' nebude fungovat podle očekávání.
* Program proto upravíme pro výstup počtu řádků jako ladící informace na standardní chybový výstup.
#include
int main(void)
{
int r = getchar();
r = r - '0';
for (int i = 0; i < r; ++i) {
printf("I like B0B36PRP!\n");
}
fprintf(stderr, "%d\n", r);
return r < 0 || r > 9;
}
* Otestujte si chování program pro různé vstup a počítání výstupu, např. ''./program out.txt''.
* Přesměrujte standardní chybový výstup do souboru např. ''./program err.txt''. Obsah souborů můžete zobrazit příkazem ''cat err.txt'' a obsah aktuálního pracovního adresáře např. ''ls -l''.
==== Formátovaný vstup a výstup ====
* Předchozí příklad modifikujte o načtení počtu opakovaného vypsání zprávy funkcí ''scanf()'' (viz ''man scanf'') včetně ošetření chyby načtení vstupu.
#include
int main(void)
{
int ret = 0;
int n;
int r = scanf("%d", &n);
if (r == 1) {
for (int i = 0; i < n; ++i) {
printf("I like B0B36PRP!\n");
}
} else {
fprintf(stderr, "ERROR: Wrong input!\n");
ret = 100;
}
return ret;
}
* V programu můžete vložit ladící informace pro výpis načtené a návratové hodnoty.
int n;
int r = scanf("%d", &n);
fprintf(stderr, "DEBUG: r: %d n: %d\n", r, n);
* Program vyzkoušejte pro různé vstupy a počítejte řádky například příkazem ''wc'':
clang program.c && echo 4 | ./a.out | wc
* Program upravte tak, že bude akceptovat pouze hodnoty v rozsahu 1 až 10. V případě načtení celého čísla mimo interval vypište na ''stderr'' zprávu ''"ERROR: Out of range!\n"'' a program vrátí hodnotu ''101''.
if (n >= 0 && n <= 9) {
...
} else {
fprintf(stderr, "ERROR: Out of range!\n");
ret = 101;
}
* Program zpřehledněte zavedením funkce ''void print_line(int n);'' s následující definicí.
void print_line(int n)
{
for (int i = 0; i < n; ++i) {
printf("I like B0B36PRP!\n");
}
}
* Program vyzkoušejte pro různé kombinace vstupu včetně přesměrování a přesměrování výstupu.
Vyzkoušejte program ukončení vstupu bez zadání znaku, tzv. ''EOT'' znakem ([[https://en.wikipedia.org/wiki/End-of-Transmission_character]]), který zadát kombinací ''Ctrl+D'' nebo ''Ctrl+Z'' podle použitého OS.
==== Procvičování formátovaného výstupu a vstupu ====
* Vyzkoušejte různé formy výpisu znaků a čísel.
char c = 'a';
int i = 1000,j;
float x,y = 3.1415;
printf("%c\n", c);
printf("%i\n", c);
printf("%d\n", i);
printf("%+5d\n", i);
printf("%x\n", i);
printf("%f\n", y);
printf("%10.3f\n", y);
printf("\t%-g\n", y);
* Co bude výsledkem výpisu následujících příkazů?
printf("%6d,%4d", 86, 1040);
printf("\n");
printf("%12.5e", 3.14159265);
printf("\r");
printf("%.4f\n", 85.167);
* Vyzkoušejte výpis na standardní výstup (''stdout'') a standardní chybový výstup (''stderr'') funkcí ''fprintf''.
fprintf(stdout, "Standard output: %d\n", i);
fprintf(stderr, "Error output: %d\n", i);
* Vyzkoušejte přesměrování výstupů programu do souboru.
./main >output.log 2>error.log
* Vyzkoušejte načtení vstupu.
1-20.3-4.0e3
* příkazem
scanf("%d%d%f%f", &i, &j, &x, &y);
* Zamyslete se nad chováním příkazu ''scanf'' na základě formátovacího řetězce, otestujte chování.
* Vyzkoušejte načtení vstupů " 9/ 16" a " 9 / 16" pomocí formátovacího řetězce ''"%d/%d"''.
* Vyzkoušejte přesměrování vstupu programu.
./main
==== Další úkoly (na doma) ====
* Napište program ''out.c'', který vypíše na výstup zadaný počet řádku v rozsahu 0 až 100, kde výstup tvoří posloupnost čísel od 1 do n.
* Program zkompilujte do spustitelného souboru ''out'' a vyzkoušte přesměrování výstupu do jiného programu nebo souboru, např.
echo 10 | ./out
echo 10 | ./out | wc
echo 10 | ./out > output.txt
cat output.txt
wc output.txt
* Napište program ''in'', který očekává na vstupu posloupnost celých čísel a po přečtení vstupu vypíše počet načtených celých čísel
* V programu ošetře chybu načtení a uzavření vstupu spolu s indikací chyby návratovou hodnotou program ''100'' a ''101'' např.
int r, n;
while ((r = scanf("%d", &n)) == 1) {
// increment of the counter
}
if (r == 0) {
fprintf(stderr, "ERROR: Wrong input detected!\n");
ret = 100;
} else {
fprintf(stderr, "ERROR: Number lines %d!\n", counter);
ret = 101;
}
* Program zkompilute do spustitelného souboru ''in'' a vyzkoušejte interaktivní režim s ručním vkládáním vstupu
* Vstup ukončíte znakem ''EOT'' (End-of-Transmission) ''Ctrl+D''.
* Program vyzkoušejte program pro různé vstupy i necelá čísla, ideálně se vstupem ze souboru např.
echo 10 | ./out > output.txt
./in < output.txt
* Programy ''out'' a ''in'' zkombinujte a ověřte jejich správné chování pro různé vstupy.
* Program ''out'' upravte pro výpis celých čísel a to v podobě ''printf("%d\n", i);'' i ''printf("%d ", i);'', kde ''i'' je řidící proměnná ''for'' cyklu.
* Program pak můžete řetězit např. ''echo 10 | ./out | ./in''.
* Dále můžete programy rozšířit o omezení rozsahu vstupu např. na celá čísla 0 až 100 nebo maximální počet vstupních řádků.