- Deklearce a přístup obdobně jako
struct
- Položky se ale v paměti překrývají - překryv od počáteční adresy
- Velikost proměnné typu union odpovídá největší položce (+ případné zarovnání)
- Pozor: dochází k reinterpretaci bitových dat
- potenciální zdroj chyb a problémů
- často kombinováno jako podčást struct s další položkoou obsahující typ dat uložeých v union
union energy_t {
int iEnergy;
float fEnergy;
unsigned char bEnergy[10];
}
- Možnost snadno přistuppovat k jednotlivým bajtům většícho typu
union intByte {
intIvalue;
unsigned char bValue[sizeof(int)];
}
- problém s manipulací, endianitou, …
- Koncept standardního vstupu a výstupu
- program nemusí vědět, kdo mu dává vstupní data
- program nemusí vědět, kam vypisuje výstupní data
- Zařízení I/O lze snadno zaměnit
- stdin ze souboru
- Windows:
program.exe < soubor.txt
- Unix:
./program < soubor.txt
- stdout do souboru
- Základní možnosti vstupu a výstupu
- výstup na obrazovku (
puts, printf)
- vstup z klávesnice (
getc, scanf)
- Funkce pro vstup a výstup jsou poskytovány standarní knihovnou
stdio.h
- nejsou tedy přímo součástí jazyka
- skoro vždy součástí - výjimka embedded platformy nebo jádro OS (
gcc -nostdlib - bez standardních knihoven)
- Binární data
- jaké bajty zapíšeme, takové přečteme
- Textová data
- na nejnižší úrovni stále binární data, ale interpretovan jako text
- pro vyjádření běžného textu
- Písmena, číslice, mezery, oddělovače, závorky, …
- tisknutelné znaky (
ASCII >= 32)
- Textová data na rozdíl od binárních přímo intepretueme
- s bajtem o hodnotě 71 jako písmenem G
- Jak interpretovat ostatní netextové znaky?
printf("Prvni radek \n druhy radek");
- nový řádek v C je speciální znak (
\n)
- Nový řádek - implementačně závislé na OS
- Unix:
\n (ASCII = 10, newline)
\n posun dolů o jeden řádek
- Windows:
\r\n (ASCII = 13 10)
\r carriage return - návrat válce psacího stroje doprava
- Data mezi producentem a spotřebitelem nemusí bý přenesena ihned
- text na obrazovku vypisován po řádcích
- data na disk zapisován po blocích
- z optimalizačních důvodů se nevolá spotřebitel pro každý elementární znak
- Produkována dta jsou ukládána do vyrovnávací paměti (tzv. buffering)
- vyčtení proběhne při jejím zaplnění (nastavení aplikace nebo OS)
- nebo externím vynucením (fflush(stdout))
- Mezi symbol
% a symbol typu lze umístit dodatečné infromace
- Počet desetinných míst
- defaultně 6 desetinných míst
%.2f - 2 desetinná místa
- Zarovnání výpisu na zadaný celkový počet cifer
%10f
- Alespoň 10 znaků (včetně tečky), pokud méně, tak doplnění pokud více, tak se neořezává
- ukazatel
- soustavy
%d - desítková
%o - osmičková
%x - šestnácková (často 0x%x)
- viz funkce s proměnným počtem paramentrů -
va_arg(arg,int)
int scanf(char *format, ...)
- analogie pro načítání ze vstupu
- Více hodnot musí být odděleno bílým znakem (mezera, tabulátor)
- pči čtení jsou bílé znaky zahazovány
EOF == -1
- Ošetření vstupních dat je velmi důležitá věc
- umožňuje korektně upozornit uživatele
- zamezuje nechtěnému chování
- zamezuje záměrnému útoku na program
- scanf a řetezěc:
scanf("%s", smallString);
- řetězec má omezenou délku, zadaný vsupt může být delší
%50s omezí načtenou délku na 5é znaků (pak ale na 51 koncová nula)
- “Fuzzing” testování
- Zašlete programy náhodný vstup
- Různá délka (i 100kB), různý obsah
- Program padá => problém
- Radamsa, MiniiFuzz, Peach, …
- namísto i/o lze použí i pole znaků
int sprintf(char *str, const char *format, ...);
- stejné jako printf, ale výstup jde ale do řetězce
- vrací počet zapsaných znaků
- pozor na celkovou délku výstupu
int sscanf(const char *str, const char *format, ...);
- stejné jako načítání z řetězce
- vrací počet načtených položek (ne znaků)
- Bezpečnější varianty často zneužívaných funkcí
- Kontrola mezí při manipulaci s řetězci, lepší ošetření chyb
- dostpuné také v novém C standardu ISO/IED 9899:2011
- Microsoft překladač obsahuje dodatečně rozšířené bezpečností varianty běžných CRT funkcí
- MSVC překladač vypíše varování C4996, o něco více pokrytých funkcí než v C11
- Standard specifikuje jejich implementaci jen jako volitenou (optional)
- Některé překladače (např. GCC) neimplementují - kód není volitelný
- Není jednotný názor na jejich použití
- Soubory obsahující binární data
- při zápisu a čtení ukládána data přesně tak, jak je zadáte
- Soubory obsahují textová data
- při čtení a zápis může docházet k nahrazení některých bajtů
- otevřeme soubor (připojíme se k souboru)
fopen()
- získáme ukazatel na soubor (
FILE *)
- Čteme/zapisujeme z/do souboru
fscanf(), fprintf(),fread(), fwrite()
- využíváme ukazatel na soubor
- Ukončíme práci se souborem (zavřeme soubor)
- na základě požadovaného chování
- chceme číst z existujícího souboru?
"r"
- Chceme vytvořit nový soubor a zapisovat do něj?
"w"
- Chceme zapisovat na konec existujícího souboru?
"a"
- Chceme číst i zapisovat do nového souboru?
"w+"
- Chceme číst i zapisovat do existujícího souboru?
- čtení i zápis kdekoli
"r+"
- čtení kdekoli, zápis vždy na konec
"a+"
FILE *fopen(const char *filename, const char *mode);
- filename- cest k souboru
- Handle je reference na nějaký ecterní prostředek
- Soubor, otevřené spjení na server nebo databáze…
- Typicky při interakci s okolím programu (OS, DB, síť, …)
- Používá se, když nelze/nemá význam přímo adresa
- Handle obdržíme po zavolání příslušné funkce
- Např.
handle = fopen("test.txt", "r");
- Hodnota “handlu” je různá a typicky ji v pragramu nezkoumáme
- Po otevření souboru je interně uchována aktuální pozice souboru (začátek - “r” a “w”; konec - “a”)
- Čtení a zápis probíhá na aktuální pozici
- Při čtení/zápisu dochází automaticky k posounu o přečtené/zapsané znaky
- Zjištění aktuální pozice
long int ftell( FILE * stream );
- POZOR: funguje jen na běžných souborech, ne na proudech (např. standardním vstupu stdin)
int fclose ( FILE * stream );
- Zavře soubor asosiovaný s ukazatelem stream
- vrací 0 pokud OK
- i v případě chyby přestane být stream asociovaný se souborem
- Při ukončení programu, jsou automaticky uzavřeny všchny otevřené soubory
- Otevřené soubory nesou režii na straně OS
- může dojít k vyčerpání systémových prostředků
- Již nepoužívané soubory zavírejme
- Detekce Valgrindem
- Čtení z aktuální pozice v soubru
- po přečtení se pozice posune těsně za přečtená data
- jeden znak
int fgetc ( FILE * stream );
- načtení jedné řádky (ukončené \n)
char * fgets ( char * str, int num, FILE * stream );
- Formátované čtení do proměnných
int fscanf( File * stream, const char * format, ... );
- Blokové čtení na binární úrovni
size_t fread( void * ptr, size_t size, size_t count, FILE * stream );
- načte block bajtů o zadané délce:
size * count
- na aktuální pozici
- jeden znak
int fputc (int character, FILE * stream);
- Zápis řetězce
int fputs(const char *str, FILE * stream);
- pokud chceme zapsat řádku, ukončíme řetězec
"\n"
- Formátovaný zápis
int fprintf(FILE * stream, const char * format, ...);
- Blokový zápis na binární úrovni
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
- zapíše block bajtů o zadné délce:
size * count
- Akuální pozici lze měnit bez čtení/zápisu
int fseek(FILE * stream, long int offset, int origin);
- zadaný offset vzhledem k origin
SEEK_SET - začátek souboru
SEEK_CUR - akuální pozice
SEEK_END - konec souboru
void rewind(FILE * stream);
- přesune aktuální ukazatel na začátek souboru (POZOR: funguje je na něžných souborech a ne na proudech (stdin, …))
- Standardní soubory
- Automaticky otevřeny a zavřeny
printf() == fprintf(stdout)
scanf() == fscanf(stdin)
getchar() == getc(stdin)
int remove( const char * filename); - odstranění souboru dle jména (cesty)
int rename (const char * oldname, const char * newname); - přejmenování souboru (pokud již nějaký s novm jménem existuje, smaže se)
FILE *tmpfile(void);
- otevře dočasný unikátní soubor
- automaticky zaniká při konci programu
- Použivejme konstantu
EOF (End of File == -1)
- V dokumentaci ke konkrétní funkci je uveden případ výskytu a použití
- hlavičkový soubor
wchar.h