Vstup a výstup standardní - obvykle klávesnice / obrazovka - každý program v jazyce C má standardně otevřen standardní vstup stdin, standardní výstup stdout a standardní chybový výstup stderr. - ty jsou obvykle napojeny na klávesnici a terminál. - standardní vstup a výstup používá vyrovnávací paměť obsahující jeden textový řádek. Při volání funkcí standardního vstupu/výstupu musíme použít hlavičkový soubor stdio.h. 12 - pokud chci použít příkazy pro práci se soubory pro výstup na obrazovku a vstup z klávesnice musím tzv. standardní vstup / výstup: - v stdio.h jsou předpřipraveny tyto proměnné: FILE *stdin; // standardní vstup FILE *stdout; // standardní výstup FILE *stderr; // standardní výstup pro chybové hlášky - tzv. standardní vstupní a výstupní proud (input / output stream) - potom souborové funkce fungují i pro klávesnici/obrazovku - čtení jednoho znaku ze standardní vstupu (klávesnice): c = getc(stdin); /*ctu z klávesnice jako getchar */ - zápis jednoho znaku do standardního výstupního proudu: putc(c, stdout); /*zap.na obrazovku jako putchar */
Příklad použití standardního vstupu/výstupu: #include <stdio.h> int main(void) { FILE *fw; int c; printf("stiskni \"O\" pro vystup na obrazovku, jina klavesa = vystup do souboru: "); c = getchar(); while(getchar()!= '\n') // viz dříve-vyčištění bufferu klávesnice ; if ((c == 'o') (c == 'O')) { fw = stdout; else { if ((fw = fopen("opis.txt", "w")) == NULL) { printf("soubor OPIS.TXT se nepodarilo otevrit\n"); system("pause"); return; /* ukonceni programu */ /* konec else */ printf("\npiste znaky a zadavani ukoncete znakem \"*\" : "); while ((c = getchar())!= '*') putc(c, fw); // využíváme zkrácené vyhodnocení podmínky, je-li fw == stdout, první podm. // je 0 a fclose se už vůbec neprovede if ((fw!= stdout) && (fclose(fw) == EOF)) { printf("soubor OPIS.TXT se nepodarilo uzavrit\n"); system("pause");
return; putchar('\n'); system("pause"); Poznámka: - pokud potřebuji zjistit, zda nějaký soubor existuje (např. abych ho nepřepsal), zkusím ho napřed otevřít pro čtení a pokud to jde (fopen nevrátí null) => soubor existuje. Zavřu ho a zeptám se uživatele zda ho chce přepsat (například). Typová konverze - změna jednoho datového typu na jiný - známe dva základní implicitní ("sama od sebe") a explicitní (výslovně vyžádána programátorem) - C je málo přísné na typové kontroly = dokáže často datové typy přizpůsobit (C je jazyk se slabou typovou kontrolou) - výhoda volnost pro programátora (hlavně v nízkoúrovňovém programování) - nevýhoda přílišná volnost může k nepříjemným chybám (projeví se při programování aplikací na vyšší úrovni - win) Implicitní double d; d = 3; /* implicitní typová konverze int => double */ d = 3 * 2.5; (pozn. zobrazení int v paměti se musí převést na zobrazení double čísla v paměti viz později)
- "priorita" konverzí aneb co se konvertuje na co (které typy jsou vyšší) v případě, že se někde "potkají" dva datové typy (při matemat. operacích apod.): int => unsigned int => long => unsigned long => float => double => long double - tj. bude-li: float a; double b, c; c = a + b; - a se bude konvertovat na double a pak se provede součet, výsledek součtu bude typu double - pokus se někde vyskytne typ char nebo short int, automaticky se konvertují na int, např.: int c; /* vetsinou znaky */ c++; c = 'A' + 3; Pozor na konverze u typů signed a unsigned: unsigned char uc; signed char sc; uc = 0; /* nejmenší */ uc = 255; /* největší */ sc = -128; /* nejmenší */ sc = 127; /* největší */ sc = 127; sc = sc + 1; /* v sc bude -128 */ Pozn. někdy starší programy u velkého disku hlásí: Zbývá -1525276 Bytů. Explicitní - přímo ji požadujeme pomocí operátoru přetypování:
(datový_typ) prom (datový_typ) konstanta - používá se hlavně, když nuceně a záměrně konvertuji nějaký vyšší typ na nižší (např. double na int) tj. když dochází k nějakému omezení rozsahu, přesnosti atp. Např.: int i; double d = 3.14; i = (int) d; /* i bude 3, d bude stále 3.14 */ (pozn. d se samozřejmě nemění, nic s ním nedělám) char c; c = (char) i; - (double), (float) atd. na stejném principu - přetypování má vysokou prioritu i = (int) d * 5; /* napřed se provede (int) d a potom nás.*/ - jinak pouze pomocí závorek: i = (int) (d * 5); /* zde se napřed vynásobí, potom konv.*/
Pozor, připomínka ze začátku semestru: double d; double PI = 3.14159; d = 4 / 3 * PI; /* int / int celočíselné dělení s výsledkem 1 */ - d bude PI, tj. 3.14159 Správně např. s explicitní typovou konverzí: double d; double PI = 3.14159; d = (double) 4 / (double) 3 * PI; - d bude 5,1887866666666666666666666666667 - nebo bez konverze rovnou a lépe: double d; double PI = 3.14159; d = 4.0 / 3.0 * PI; Speciální případ přetypování na typ void - void - prázdný typ, někdy obecný typ Užití např.: - potlačení návratové hodnoty funkce (void) sin(pi); /* poněkud málo užitečné použití ;-) */ - v případě pointerů ukazatelů na proměnnou, je to proměnná obsahující adresu jiné proměnné, musí se dělat na konkrétní datový typ: int *p_i; /* p_i bude obsahovat adresu proměnné typu int */ void *p_cokoliv; /* pointer na jakýkoliv typ, univerzální pointer */ - při použití je nutno potom přetypovat na konkrétní datový typ. int i;
p_cokoliv = (void *) &i; p_i = (int *) p_cokoliv; - vícenásobné přetypování zde použito na odříznutí desetinných míst double d; d = 13.628346; d = (double)((long int) d); /* d bude 13.0 */ - pozor, aby se double číslo po konverzi na např. int, do tohoto typu "vešlo", aby nebylo moc velké Např. udělat test typu: if (d > (double) INT_MAX) (pro minimum je konstanta INT_MIN) pozor názvy konstant se mohou lišit dle překladače (nutno naincludovat limits.h) (int) f = 3; /*fuj! nelze, přetypování NENÍ l-hodnota */
Preprocesor jazyka C - direktivy začínají # - zpracují před vlastním překladem do stroj. kódu - např. #include Konstanty - pomocí direktiv - název konstanty obvykle velkými písmeny - překladač (preprocesor) před překladem projde zdrojový kód a nahradí všechny výskyty názvu konstanty její hodnotou (jako funkce Najít a nahradit ve Wordu atp.) #define PI 3.14 #define PIPI (PI + PI) /* zde jsou závorky NUTNÉ!!! */ main() { double d; d = 2.0 * PIPI; /* bez závorek u PIPI by počítal: 2 * 3.14 + 3.14 */ printf("\nd = %f\n", d); system("pause"); - zrušení definice: #undef PI
Lze též konstantu definovanou, ale s neurčenou hodnotou: #define WOKNA main() { double d; d = 2 * PIPI; printf("\nd = %f\n", d); #ifdef WOKNA printf("\njsem pod Woknama... UAAAA!!!\n"); #else Tady dam cokoliv, sem se prekladac nedostane!!! asdf printf("\nnejsem pod Woknousama, huraaa...\n"); #endif - pozor příkazy s # jsou directivy, nikoli příkazy jazyka C, tj. říkají ne kudy program běží (třeba onen ifdef), ale co se překládá a co ne. lze též: #ifndef WOKNA - zbytek je stejný Nebo: #define WIN Sedum main() { double d; d = 2 * PIPI; printf("\nd = %f\n", d); - tj. if not def - jestliže není def. #if WIN == Sedum printf("\njsem pod Woknama Sedum... UAAAA!!!\n");
#else Tady dam cokoliv, sem se prekladac nedostane!!! asdf printf("\nnejsem pod Vistama, huraaa...\n"); #endif - konstanty nemusí jen číselné, lze znaky, řetězce a též operátory: #define MOD % - potom místo a % b lze používat a MOD b #define KONEC_RADKU '\n' - nebo i vypočtené hodnoty: #define POSUN ('a' - 'A') Makra - dělají se také pomocí #define - podobné (použitím) funkcím - předpřipravená jsou např. v ctype.h - jak na vlastní makro: #define je_velke(c) ((c) >= 'A' && (c) <='Z') - a potom použití v programu: if (je_velke('a' + 'B')) {.. - před překladem z toho preprocesor udělá toto: if ((('A' + 'B') >= 'A' && ('A' + 'B') <='Z')) {..
- opět je nutno závorkovat jak tělo makra, tak jeho parametr uvnitř těla Makra v ctype.h např.: isalnum(c) je číslice nebo písmeno? isalpha(c) je písmeno? isascii(c) je ascii 0-127? iscntrl(c) je ascii 0-26? např. test jen na tisknutelné znaky bez háčků a čárek: if (isascii(c) &&!iscntrl(c)) { putchar(c); isdigit(c) je číslice? islower(c) je malé písmeno? isupper(c) je velké písmeno? isprint(c) je ascii 32-126 (tisknutelné)? ispunct(c) je interpunkční znaménko? (,. / atp.) isspace(c) je mezera, tab, \n tj. nový řádek? isxdigit(c) je hexadec. cislice (0-9, A-F, a-f)? isgraph(c) je ascii 33-126 (i pseudografické znaky)? Konverzní makra tolower(c) - na malá písmena toupper(c) - na velká písmena toascii(c) - ořízne bit s pořadím 7 (tj. osmý, nejvyšší bit), tj. z 8 bitů znaku bere jen dolních 7, tj. 0. až 6.
Paměťové třídy proměnných auto - automatické prom. - ve funkcích automaticky extern - pro globální, "sdílené" proměnné - mezi několika soubory.c - v jednom je např. int globalni; a ve všech souborech.c téhož projektu musí být: extern int globalni; - jinak by došlo k vícenásobné deklaraci té samé proměnné a linker by nahlásil chybu static - většinou lokální proměnné, které si zachovají hodnotu mezi voláními téže funkce tj. automatické (obyčejné lokální proměnné) po skončení funkce zaniknou. static zůstane = přežije do příštího volání své funkce a pamatuje si svoji hodnotu. Např.: funkce si bude pamatovat, kolikrát byla volána: int namahana(int a) { static int kolik_volani = 0; kolik_volani++; printf("\nuff, uz mne volali %d-krat...", kolik_volani); return a*a; register - žádost nebo doporučení pro překladač, aby umístil proměnnou do registru procesoru místo do paměti (ten to ovšem nemusí respektovat) - nelze u globálních proměnných register int reg;
Typové modifikátory volatile - znamená: neoptimalizuj proměnnou, může změněna nějakou asynchronní událostí volatile int i; const konstanty, v některých verzích const double pi = 3.14159; - dále lze využít u parametrů funkcí: int najdi(const char *str, char co) { neco dela - nelze změnit řetězec str uvnitř funkce Inicializace proměnných - opakování - C neinicializuje proměnné int i = 10; // inic. v deklaraci nebo int i; // ale u static se chová jinak!!! viz výše i = 10;
Jak se zobrazovala čísla v paměti vývoj - vždy je nutno říci na kolika bitech počítám viz bitová negace - ukázky níže jsou na 8 bitech 1. Dekadická čísla a) rozložený tvar 12345 5 4 3 2 1 00000101 00000100 00000011 00000010 00000001 b) stažený tvar Binary Coded Decimal 12345 D 45 23 01 0100 0101 0010 0011 0000 0001
Převody číselných soustav 11111111 B = FF H = 255 D 5 D = 101 B 76543210 pořadí bitů 00000101 B = 2 0 *1 + 2 1 *0 +2 2 *1 + 2 3 *0 + 2 4 *0 +2 5 *0 + 2 6 *0 + 2 7 *0 = 1 + 0 + 4 + 0 + 0 + 0 + 0 + 0 = 5 D 10 D = 1010 B 40 D = 28 H 54 D = 36 H 31 D = 1(15) = 1F H 2 3 0 0 Číslice 16ové soustavy: 0 1 2 3 4 5 6 7 8 9 (10) (11) (12) (13) (14) (15) 0 1 2 3 4 5 6 7 8 9 A B C D E F 1Fh = 1F H 2) Celá čísla v pevné řádové čárce (pevná délka slova) a) jednoduchá délka Word = 2 Byte 0 65535 b) dvojnásobná délka DWord = 4 Byte 0 4294967295
Na 8 bitech: MSB LSB n-1 0 Most Significant Bit nejvýznamnější bit Least Significant Bit nejméně významný bit Zobrazení kladných i záp. celých čísel v paměti počítače a) přímý kód (příklady na 8mi bitech, obdobně pro více bitů) 5 D = 00000101 B kl. čísla 0 127 záp. čísla 127 0 0/1 +/- Nevýhodné mám dvě nuly: 00000000 B = +0 D 10000000 B = -0 D b) invezní kód 5 D = 00000101 B -5 D = 11111010 B Nevýhodné mám dvě nuly: 00000000 B = +0 D 11111111 B = -0 D
c) doplňkový kód 5 D = 00000101 B 11111010 inverzní kód + 00000001-5 D = 11111011 B Když chci získat dec. číslo: -5 D = 11111011 B 00000100 inverzní kód + 00000001 00000101 B = 5 D => převáděl jsem ze záp. -5 D 5 D = 00000101 B -5 D = 11111011 B 00000000 B 00000000 B = 0 D 11111111 B = -1 D 11111111 B 00000000 B 00000001 B 00000001 B 00000000 B 00000001 B = 1 D 127 + 1 dojde k přetečení 10000000 = -128 D 01111111 00000001 10000000 = 128 D
Zobrazování čísel v paměti počítače - dokončení Celá čísla - dokončení d) kód s posunutou nulou někde uprostřed rozsahu si stanovím nulu, např. 127 11111110 B = 254 D 127 (posunutá 0) = 127 D 11111100 B = 252 D 127 (posunutá 0) = 125 D 00000 101 B = 5-127 (posunutá 0) = -122 D 01111110 B = 126-127 (posunutá 0) = -1 D 01111111B = 127-127 (posunutá 0) = 0 D 0 127 128 255 p.n.-127 volím 0 128 p.n.-128 volím 0 127 3) Reálná čísla a) pevná řádová čárka,00100000 B = 0,125 D 76543210 1 1* - + atd. 2 8-5 b) plovoucí řádová čárka formát dle normy IEEE 754-0,234. 10 5 32 bitů 00000000000000000000000000000000 znaménko mantisy - 1 bit (1 = mínus, 0 = plus) exponent - 8 bitů celé číslo - kód s posunutou nulou mantisa 23 bitů (1 bit skrytý s hodnotou 1) - přímý kód desetinné binární č. 64 bitů 0000000000000000000000000000000000000000000000000000000000000000 znaménko mantisy - 1 bit (1 = mínus, 0 = plus) exponent - 11 bitů celé číslo - kód s posunutou nulou mantisa 52 bitů (1 bit skrytý s hodnotou 1) - přímý kód desetinné binární č.
Např. : 13.157 D = 1101.00101000001100010001 B tj. 1.10100101000001100010001 B 2 3 Znaménko: 0 Exponent: 3 + 127(posunutá nula) = 130 D = 10000010 B Tj. v paměti bude uloženo (na 32 bitech, tj. typ float) 01000001010100101000001100010001 viz též: http://en.wikipedia.org/wiki/ieee_754-2008 http://amber.feld.cvut.cz/psp/ieee754.htm http://cs.wikipedia.org/wiki/ieee_754 http://www.root.cz/clanky/norma-ieee-754-a-pribuzniformaty-plovouci-radove-tecky/ Interaktivní demo: http://www.h-schmidt.net/floatapplet/ieee754.html