2 - Vstup a výstup programu

Cíle cvičení

  1. Kompilace a spuštění programu.
  2. Návratová hodnota programu a její zpracování v OS.
  3. První cyklus.
  4. Vstup a výstup programugetchar a standardní výstup - putchar.
  5. Formátovaný vstup a výstup - scanf,printf a fprintf.
  6. Operátory a základní datové typy - char, int, float, double.
  7. 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 <stdio.h>
 
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 $?.

  • 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 <stdio.h>
 
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 <stdio.h>
 
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 <stdio.h>
 
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 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 <stdio.h>
 
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 <in.txt; echo $?
./program <empty.txt; echo $?
echo 'a' | ./program; echo $?

  • 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 <stdio.h>
 
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 <in.txt | wc. Otestujte chování též pro záměnu stderr za stdout.
  • Přesměrujte standardní výstup do soubor např. ./program <in.txt >out.txt.
  • Přesměrujte standardní chybový výstup do souboru např. ./program <in.txt 2>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 <stdio.h>
 
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 <input.log

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ů.
courses/b0b36prp/labs/lab02.txt · Last modified: 2024/09/15 16:47 by faiglj