1 / 82

Programovací jazyk C

Ken Thompson 1943 - Dennis MacAlistair Ritchie 1941 -. Počítač PDP-7. Programovací jazyk C. http://cs.wikipedia.org/wiki/C_(programovací_jazyk).

toby
Download Presentation

Programovací jazyk C

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Ken Thompson 1943 - Dennis MacAlistair Ritchie 1941 - Počítač PDP-7 Programovací jazyk C http://cs.wikipedia.org/wiki/C_(programovací_jazyk) Operační systém UNIX byl vyvinut v sedmdesátých letech pro společnost Bell Telephone Laboratories (divize AT&T) na počítači PDP-7. V souvislosti s UNIXem byl také vyvinut programovací jazyk C. UNIX byl ve vyšších verzích psán v C-čku sám, což umožnilo jeho přenositelnost na další počítače (strojový kód je sám o sobě zpravidla nepřeno-sitelný). Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti Pro projekt „Cesta k vědě“ (veda.gymjs.net) vytvořil V. Pospíšil (gdermog@seznam.cz) a A. Šulc. Modifikace a šíření dokumentu podléhá licenci CC-BY-SA.

  2. Programovací jazyk C GNU Compiler Collection (zkráceně GCC) je sada kompilátorů vytvořených v rámci projektu GNU. Původně se jednalo pouze o překladač programovacího jazyka C (a zkratka tehdy znamenala GNU C Compiler), později byly na stejném společném základě vytvořeny překladače jazyků C++, Fortran, Ada a dalších. Původním autorem GCC je Richard Stallman, který ho roku 1987 vytvořil jako jednu ze základních částí svého projektu GNU; dnes projekt zastřešuje nadace Free Software Foundation. GCC je šířen pod licencí GNU GPL a stal se již de facto standardním kompilátorem v open source operačních systémech unixového typu, ale používá se i v některých komerčních operačních systémech, např. na Mac OS X. Existují také jeho portace pro Microsoft Windows (např. mingw). Richard Matthew Stallman 1953 - http://en.wikipedia.org/wiki/GNU_Compiler_Collection

  3. První progámek a jeho překlad Programů, které zajišťují překlad zdrojáku do strojového kódu a sestavení jednotlivých přeložených souborů do jednoho je více. My budeme používat GNU překladač gcc. Jako příklad uveďme jednoduchý prográmek, který je uložen v souboru hello.c . Co která řádka konkrétněji znamená později, program jen vypíše text „Hello, world!“ na obrazovku. Povšimněte si použití syntax highlight, který je běžný u většiny textových editorů. #include <stdio.h> #include <stdlib.h> int main( void ) { printf( "Hello, world!\n" ); } /*main*/ Překlad i linkování se zařídí příkazem g++ (který je součástí systému gcc) g++ -c hello.c g++ -o hello hello.o Výsledkem bude binární soubor hello.o a spustitelný program hello.

  4. Skripty pro překlady Zdrojový kód 1 #define itemDIRECTORY "directory" #define itemVOTES "votes" #define itemLIST "list" #define itemFILE "file" #define itemOUTPUT "output" #define itemUNVALID "unvalid" #define MAX_NR_OF_CANDIDATES 100 #define cand2D( x, y ) ( MAX_NR_OF_CANDIDATES*(y) + (x) ) class CElectionProcessor { public: CElectionProcessor( char *descFileName, CCategory *cat = NULL, CEntryList *ent = NULL, CVoteList *vot = NULL); ~CElectionProcessor( void ); void WriteElectionSetup( FILE *targetFILE = stdout ); int Run( FILE *out ); char *GetVoteListFileName( void ) { return voteListFileName; }; char *GetMailDirectory( void ) { return mailDirectory; }; char *GetOneMailFileName( void ) { return oneMailFileName; }; char *GetMailFileNameList( void ) { return mailFileNameList; }; char *GetOutputFileName( void ) { return outputFileName; }; Je-li souborů se zdrojovým kódem více, je výhodné použít skript Makefile nebo další nástroje jako CMake (www.cmake.org) nebo SCons (www.scons.org). Sestavování (link) Překlad jednoho souboru = jedno zavolání překlada-če. Velké programy mají obvykle stovky zdrojových souborů. Přeložený modul 1 Program ve strojovém kódu Přeložený modul 2 Překládat lze navíc něko-lika režimy (pro debug-ging, pro různé typy počí-tačů), nebo jenom změně-né části kvůli úspoře času. To nelze ručně. Přeložený modul 3 Zdrojový kód 2 Překlad (compile) Zdrojový kód 3

  5. Skripty pro překlady Zdrojový kód 1 #define itemDIRECTORY "directory" #define itemVOTES "votes" #define itemLIST "list" #define itemFILE "file" #define itemOUTPUT "output" #define itemUNVALID "unvalid" #define MAX_NR_OF_CANDIDATES 100 #define cand2D( x, y ) ( MAX_NR_OF_CANDIDATES*(y) + (x) ) class CElectionProcessor { public: CElectionProcessor( char *descFileName, CCategory *cat = NULL, CEntryList *ent = NULL, CVoteList *vot = NULL); ~CElectionProcessor( void ); void WriteElectionSetup( FILE *targetFILE = stdout ); int Run( FILE *out ); char *GetVoteListFileName( void ) { return voteListFileName; }; char *GetMailDirectory( void ) { return mailDirectory; }; char *GetOneMailFileName( void ) { return oneMailFileName; }; char *GetMailFileNameList( void ) { return mailFileNameList; }; char *GetOutputFileName( void ) { return outputFileName; }; Je-li souborů se zdrojovým kódem více, je výhodné použít skript Makefile nebo další nástroje jako CMake (www.cmake.org) nebo SCons (www.scons.org). Přeložený modul 1 Sestavování (link) Program ve strojovém kódu Přeložený modul 2 za běhu programu Přeložený modul 3 Dynamicky linkovatelná knihovna (.dll ve windows, .so pod linuxem) Zdrojový kód 2 Překlad (compile) Zdrojový kód 3

  6. Skripty pro překlady Nyní se podívejme, jak by se překládal a linkoval program, který má více zdrojových souborů. Řekněme, že struktura zdrojáků je následující: /inc/main.h main.o /src/main.cpp binary /inc/module.h module.o /src/module.cpp překlad linkování Překlad i linkování se zařídí příkazem g++ následovně g++ -c /src/module.c g++ -c /src/main.c g++ -o binary main.o module.o Vidíme, že složitost příkazů narůstá. Pokud by zdrojáků bylo dvacet, byl by překlad již velmi komplikovaná záležitost.

  7. Skripty pro překlady - Makefile Práci nám usnadní program make. Předpokládejme strukturu /inc/main.h main.o binary /inc/source1.h /src/main.cpp Skript pro make se jmenuje Makefile. Popisuje, které soubory se mají jak překládat a říká programu make, co má přeložit, pokud se některý soubor změní. Například byl-li změněn od posledního překla-du pouze soubor source2.h, program make provede pouze příkaz g++ na šestém řádku. Příkaz make lze zavolat přímo se jménem modulu, např. make source1 a pak se provede pouze příslušná část skriptu. Pozn.: před příkazy g++ (případně jinými) v jednotlivých oddílech skriptu musí být znak „tabelátor“. /src/source1.cpp source1.o /inc/source2.h /src/source2.cpp source2.o binary: main.o source1.o source2.o g++ $(CFLAGS) -o binary main.o source1.o source2.o main: ./inc/main.h ./src/main.cpp g++ $(CFLAGS) -c ./src/main.cpp source1: ./inc/source1.h ./src/source1.cpp g++ $(CFLAGS) -c ./src/source1.cpp source2: ./inc/source2.h ./src/source2.cpp g++ $(CFLAGS) -c ./src/source2.cpp clean: rm –f *.o *.d

  8. Skripty pro překlady - Odkazy Příklad složitějšího skriptu Makefile: http://veda.gymjs.net/ Podrobější informace o Makefile na webu (EN): http://en.wikipedia.org/wiki/Make http://www.gnu.org/software/make/manual/html_node/index.html http://www.opussoftware.com/tutorial/TutMakefile.htm Alternativní nástroje k Makefile CMake - http:// www.cmake.org/ SCons - http:// www.scons.org/

  9. Základní syntax 1) 2) 3) 4) 5) 6) 7) 1) Direktiva preprocesoru (začíná znakem #). Zde je vnořen soubor stdio.h 2) Deklarace funkce. Speciálně funkce main je vstupní bod programu. 3) Blok příkazů (např. tělo funkce).Začíná znakem { a končí znakem } . 4) Deklarace proměnné. Zde je deklarována proměnná count, která má celočíselný typ int. 5) Cyklus typu for. Proběhne 500x . 6) Příkaz vykonaný v každém průběhu cyklu. 7) Konec funkce, návratová hodnota je 0.

  10. Komentáře Komentáře dovolují programátorovi vpisovat do zdrojového kódu poznámky, díky nimž se kód stane přehlednějším. Lze do nich také vpisovat například „TO DO“ poznámky nebo dočasně vyřadit část kódu pro potřeby testování. #include <stdio.h> intmain( void ) { // Funkce main je vstupní bod programu intcount; // Deklarace řídící proměnné cyklu for( count = 1; count <= 500; count++ ) printf( “I will not throwpaperairplanes in class. \n“ ); // Cykl proběhne 500x a vypíše zprávu return 0; // Konec programu, návratová hodnota je 0 } /*main*/ // …….. komentář až do konce řádku /* */ … cokoliv mezi těmito dvojznaky je komentář Dobrá rada : Vždy pište komentáře i do sebejednodušších programů v poměru 1 řádek kódu ku 1 řádku komentáře. Umožní to orientovat se v programu komukoliv, kdo ho dostane po vás, nebo i vám samotným po delší době.

  11. Typy a deklarace proměnných intcislo; // Deklarace celočíselné proměnné. Její hodnota je náhodná. intcislo = 1 ; // Deklarace celočíselné proměnné. Její hodnota je jedna. floatpi = 3.1415; // Deklarace reálné proměnné, její hodnota je 3.1415 double hodnota = 2.56e-5; // Deklarace reálné proměnné, její hodnota je 0.0000256

  12. Lexikální rozsah platnosti Deklarace proměnné je platná pouze ve svém bloku kódu (a všech vnořených) #include <stdio.h> intmain( void ) { intpocet = 50; for( intcount = 1; count <= pocet; count++ ) { intdruhaMocnina; intdalsiCislo; druhaMocnina = count * count; printf( “Druha mocnina %i je %i \n“, count, druhaMocnina ); dalsiCislo = count * pocet; printf( “%i * %i = %i \n“, count,pocet, dalsiCislo ); } /*for*/ pocet = 100; dalsiCislo = 20; } /*main*/ Proměnná pocet je viditelná v celém bloku funkce main (a tedy i uvnitř for cyklu). Proměnná count je viditelná pouze v bloku for cyklu. Proměnné druhaMocnina a dalsiCislo jsou viditelné pouze v bloku for cyklu. Jejich použití za tímto blokem vyvolá chybu při překladu. Chyba!

  13. Lexikální rozsah platnosti Deklarace proměnné je platná pouze ve svém bloku kódu (a všech vnořených). Pokud je ve vnořeném bloku deklarována další proměnná téhož jména, je výše deklarovaná proměnná dočasně překryta. Přístupná bude po opuštění vnořeného bloku. #include <stdio> intmain( void ) { intdalsiCislo = 500; printf( “Cyklus brzy zacne, dalsiCislo = %i \n“, dalsiCislo ); for( intcount = 1; count <= 5; count++ ) { intdalsiCislo = count + 1; printf( “count = %i, dalsiCislo = %i \n“, count, dalsiCislo ); } /*for*/ printf( “Cyklus probehl. dalsiCislo = %i \n“, dalsiCislo ); } /*main*/ Cyklus brzy zacne, dalsiCislo = 500 count = 1, dalsiCislo = 2 count = 2, dalsiCislo = 3 count = 3, dalsiCislo = 4 count = 4, dalsiCislo = 5 count = 5, dalsiCislo = 6 Cyklus probehl, dalsiCislo = 500 Dobrá rada : Jména proměnných držte unikátní v celém logickém celku (tělo jedné procedury, modul). Překrytí jmen proměnných jsou zbytečná a vedou k častým chybám.

  14. Lexikální rozsah platnosti Proměnné je možné deklarovat i na globální úrovni, tj. kdekoliv ve zdrojovém kódu mimo těla funkcí (vč. funkce main). Ty jsou pak viditelné kdekoliv od své deklarace. Dobrá rada: Deklaraci globálních proměnných je třeba si dobře rozmyslet speciálně v případech, kdy se program skládá z více souborů se zdrojovým kódem! zdroják1.c zdroják2.c int jakesiCislo; float jakesiCislo; PROBLÉM

  15. Konstanty Deklarace proměnné uvozená klíčovým slovem const se považuje za hodnotu určenou pouze ke čtení. Jakýkoliv pokus o zápis do takto deklarované proměnné vyústí v chybu při překladu. Konstanty je možné definovat i pomocí direktivy preprocesoru define. Před vlastním překladem preprocesor fyzicky nahradí každý výskyt jména konstanty udanou hodnotou. O direktivách preprocesoru bude pojednáno podrobně později. Zápis číselných konstant je dovolen v desítkové, osmičkové a šestnáctkové soustavě. Osmičkové číslo začína nulou, šestnáctkové dvojznakem 0x. Reálná čísla mohou být zapsána v exponenciální formě (se základem deset). constint pathWidth = 100; constchar tabulator = '\t' ; constdouble pi = 3.1415 ; #define pathWidth 100 #define tabulator '\t' #define pi 3.1415 • // Dekadicky • 0113 // Osmičkově • 0x4B // Šestnáctkově • 1.5e-12// Reálné číslo

  16. Konstanty Pro zápis znakových a řetězcových konstant se používají jednoduché a dvojité uvozovky. Znakové i řetězcové konstanty mohou obsahovat speciální znaky (znak se zpětným lomítkem, viz tabulka vlevo dole). Pro práci s řetězcovými konstantami platí následující pravidla : 'z‚ 'p' "Hello world" "How do you do?" Dlouhý řetězec lze na více řádek rozdělit pomocí zpětného lomítka. " Retezec, který je dlouhy a nevejde se \ na jednu radku" Lze spojit za sebou několik řetězcových konstant "toto tvori" "jeden cely" "retezec" "znaku" Řetězec rozšířených znaků je označen znakem L. L"Toto je řetězec rozšířených znaků : α, β, €, £ …" Pozor, tato pravidla neplatí pro práci s řetězcovými proměnnými (viz později)!

  17. Složené typy – statická pole Pole je vektor (soubor, souhrn, posloupnost - jak chcete) proměnných stejného typu. Tyto proměnné ovšem nejsou samostatně pojmenované, pouze oindexované a jsou přístupné přes jméno celého pole a příslušný index. Statické pole (tedy takové, u nějž je počet prvků znám již při překladu), se deklaruje takto: typ jméno [n] kde n je počet prvků. Schéma příslušné struktury lze zobrazit například takto: Pole má n prvků indexovaných od nuly. int poleCisel [10]; int jinePoleCisel [5] = { 1, 2, 3, 4, 5 } ; float realnePole [4] = { 0.1, 1.2, 2.3, 3.4 } ; float spatne [3] = { 0.1, 0.2, 0.3, 0.4, 0.5 } ; Příklady na deklaraci statických polí. prvky pole lze již při deklaraci naplnit specifikova-nými hodnotami pomocí složených závorek. Poslední z příkladů vyvolá chybu při překladu. poleCisel [3] = 12 ; jinePoleCisel [0] = poleCisel[3] + poleCisel[4] ; realnePole[4] = 1.5 ; realnePole[ poleCisel[3] ] = 10; Příklady na přístup do statických polí. Předposlední z příkladů vyvolá (pravděpodobně) chybu při překladu, poslední pak buď chybu za běhu nebo způsobí zcela iracionální a náhodné chování programu.

  18. Složené typy – Výčty Výčty jsou ordinární (celočíselné) typy, které mohou ale obsahovat pouze vybrané hodnoty. Tyto hodnoty jsou nějak pojmenované. enum jméno typu { hodnota1, hodnota2, hodnota3, . . } jméno proměnné; Například pro jména měsíců by bylo možné deklarovat výčet mesic_typ, který obsahuje jména měsíců. S těmito jmény pak lze pracovat jako s hodnotami proměnné. Pozn.: mesic je ve skutečnosti celočíselná proměnná a jména měsíců jsou zástupná jména pro celá čísla. Defaultově je první položka výčtu nula, lze to ovšem změnit přiřazením. enum mesic_typ { leden, unor, brezen, duben, kveten, cerven, cervenec, srpen, zari, rijen, listopad, prosinec } mesic; mesic = leden; mesic = unor; if( mesic == brezen ) JaroJeTady(); enum barvy_typ { bila = 1, modra = 2, cervena = 4, zelena = 8, cerna = 16 } barva; Barvy ve druhém příkladu mají jako hodnoty přiřazené mocniny dvou a je tedy možné je použít jako flags (viz binární operátory).

  19. Složené typy - Struktury Datová struktura je skupina proměnných shromážděná pod jedním jménem. Syntax její deklarace vypadá následovně: struct jméno typu { typ člen 1; typ člen 2; typ člen 3; ...} jméno proměnné; Typy jednotlivých členů můžou být libovolné základní typy nebo další složené (struktura, výčet, pole). K jednotlivým členům proměnné typu struktura se přistupuje pomocí operátoru "." (tečka): struct complex { float Re; float Im; } A, B, C; A.Re = 10.1; A.Im = -3; V příkladu nalevo jsou vytvořeny tři proměnné A, B a C jako struktury představující komplexní číslo (obsahují dvě reálná čísla, jedno pro reálnou a druhé pro imaginární část). Do proměnné A je poté uložena hodnota 10.1 - 3i .

  20. Složené typy - Struktury Se strukturou lze nakládat stejně jako se základními typy, například vytvářet pole: Zadejte nazev : Blade_Runner Zadejte rok : 1982 Zadejte Nazev : Matrix Zadejte rok : 1999 Zadejte nazev : Taxi_Driver Zadejte rok : 1976 Zadal jste nasledujici tri filmy : Blade_Runner (1982) Matrix (1999) Taxi_Driver (1976) #include <stdio.h> struct film_typ { char Nazev[100]; int Rok; } filmy [10]; int main ( void ) { for ( int n = 0; n < 3; n++ ) { printf( "Zadejte nazev : " ); scanf( "%s", filmy[n].Nazev ); printf( "Zadejte rok: " ); scanf( "%u", & filmy[n].Rok ); } printf( "\nZadal jste nasledujici tri filmy : \n" ); for ( int n = 0; n < 3; n++ ) printf( "%s (%i) \n", filmy[n].Nazev, filmy[n].Rok ); }

  21. Přetypování Převod výrazu či proměnné jednoho typu na typ jiný se nazývá přetypování (type-casting). U některých typů provádí překladač přetypování sám - tzv. implicitní přetypování : shorta = 1024; // Proměnná typu short má délku 2 byte int b ; // Proměnná typu int má délku 4 byte b = a; // Pokud přiřazujeme do proměnné typu int proměnnou typu short, dva byty proměné // short se zkopírují a dva zbývající byty proměnné int vynulují. V některých případech je ale překladači nutné říct, co přesně má na co přetypovat. Zejména je to třeba při práci s ukazateli, ale i některé základní typy to vyžadují. Přetypování lze v zásadě zapsat dvěma způsoby: inta = 1024; float b; b = (float) a; // Starší způsob vyhovující normě ANSI-C b = float (a); // Přetypování na způsob volání funkce a = (int) b; a = int (b); Převod celého čísla na reálné je relativně komplikovaná záležitost, protože organizace jednotlivých bitů ve float a int jsou zcela odlišné, ačkoliv celková délka je v obou případech stejná (4 byte).

  22. Operátory Máme-li deklarované proměnné, chceme s nimi také nějak operovat. Pro tento účel C integruje tzv. operátory. Na rozdíl od mnoha jiných jazyků nejsou operátory pojmenovány slovy, ale symboly, které se skládají nikoliv z písmen, ale jiných, na klávesnici běžně dostupných znaků. Operátorů je poměrně velký počet. Přiřazovací operátor (=) Příkaz vlevo uloží do proměnné a hodnotu 5 v prvním případě, respektive obsah proměnné b v druhém. Výraz nalevo od rovnítka se označuje jako lvalue (left value), výraz napravo jako rvalue (right value). Lvalue musí vždy být proměnná, zatímco rvalue může být jakákoliv kombinace proměnných, konstant či výsledů jiných operací. Přiřazování se vždy děje zprava doleva – hodnota proměnné vlevo se změní až po vyhodnocení výrazu napravo. Předchozí hodnota je v tomtéž okamžiku zapomenuta. a = 5; a = b; int a, b; // Deklarace, hodnoty a a b jsou nyní neurčené a = 5; // Do a je uložena hodnota 5 b = 4; // Do b je uložena hodnota 4 a = b; // Do a je uložena hodnota b, tedy 4 b = 15; // Do b je uloženo 15, v a je stále 4 (předchozí rovnítko již // nehraje žádnou roli Přiřazení není matematická rovnost!

  23. Operátory Aritmetické operátory ( +, -, *, /, % ) Aritmetické operátory fungují tak, jak od nich čekáme (součet, rozdíl, násobek, podíl). Podle typu operandů je operátor dělení celočíselný nebo reálný. Operátor % (modulo) je definován pouze pro celá čísla a celočíselné proměnné a vrací zbytek po dělení : int a, b; // Deklarace, hodnoty a a b jsou nyní neurčené a = 5 + 3; // Do a je uložena hodnota 8 b = 5 / 3; // Do b je uložena hodnota 1 a = 6 / 3; // Do a je uložena hodnota 2 b = 7 % 3; // Do a je uložena hodnota 1 float x = 6.2 / 12.8; // Do x je uložena hodnota 0.484375 x = a - b; // Do x je uložena hodnota a – b = 2 – 1 = 1. Zde dochází k automatickému // přetypování z celočíselného typu na reálný. Pozn. : Lvalue jednoho operátoru může být rvalue (nebo částí rvalue) jiného operátoru. Jsou tedy dovoleny konstrukce typu : a = 5 + (b = 3); a = b = c = d = 6; Podobné konstrukce mohou být užitečné třeba v cyklech, obecně je ale lepší se jim vyhnout, neboť znepřehledňují program.

  24. Operátory Bitové operátory ( ^, &, |, ~ ) Bitové operátory provádějí logické operace s jednotlivými bity operandů (celočíselných). Označíme-li 0 za false a 1 za true, pak: bitové exkluzivní OR respektive XOR (nonekvivalence) 0 & 0 = 0 0 & 1 = 0 1 & 0 = 0 1 & 1 = 1 0 | 0 = 0 0 | 1 = 1 1 | 0 = 1 1 | 1 = 1 0 ^ 0 = 0 0 ^ 1 = 1 1 ^ 0 = 1 1 ^ 1 = 0 bitové OR (disjunkce) bitové AND (konjunkce) Operace jsou provedeny s každý bitem operandů zvlášť, tedy například: Bitová negace (doplněk) pak převrátí smysl všech bitů, tj:

  25. Operátory Bitové operátory ( ^, &, |, ~ ) Bitové operátory ve spojení s celočíselnými proměnnými mohou tvořit účinnou a úspornou metodu ukládání skupin binárních stavů (flags). Máme-li například v experimentu částicové fyziky řadu subdetektorů (řekněmě 32) u nichž je pro každý event nutné uložit stav (zanznamenal/nezaznamenal průchod částice). Pokud bychom použili 32 proměnných typu bool, tato data by pro každý event zabrala 32 byte a pro milion událostí 32 MB, což je hodně. Přitom stav včech detektorů lze úsporně uložit do 4 byte, pokud každý bit celočíselné proměnné typu int přiřadíme právě jednomu detektoru : detektory = detektory | 4 ; // Přiřazení stavu 3. detektoru „ZÁSAH“ detektory = detektory & (~4) ; // Přiřazení stavu 3. detektoru „NIC“ stav = detektory & 4 ; // Zjištění stavu 3. detektoru NIC ZÁSAH AND OR NOT = = Obecně pro n-tý dektektor nahradíme konstantu 4 konstantou 2n. int detektory;

  26. Operátory Relační operátory Relační operátory slouží pro porovnávání číselných hodnot. Bez rozsáhlejších úprav (přetěžování) je nelze aplikovat na žádné jiné. Jejich výsledek je buď 0 (false) nebo 1 (true) : a == b // a je rovno b a != b // a není rovno b a < b // a je menší než b a <= b // a je menší nebo rovno b a > b // a je větší než b a >= b // a je větší nebo rovno b Logické operátory ( &&, ||, ! ) Logické operátory slouží pro spojování podmínek (logických výrazů) do větších celků. Operandy mohou být proměnné typu bool nebo celá čísla, kde pak 0 je považována za false a cokoliv jiného za true. Výsledek je booleanovská hodnota. Nepleťe si je s bitovými operátory! P && Q // Vrací true jsou-li P i Q pravdivé P || Q // Vrací true je-li alespoň jeden z P a Q pravdivý ! P // Vrací true je-li P nepravdivý

  27. Operátory Inkrementace a dekrementace ( ++, -- ) Operátor ++ zvýší hodnotu operandu o jedna, -- ji o jedna sníží. Existují v prefixové a postfixové variantě. Efekty jsou následující (předpokládejme, že hodnota b před operací je 10) : b++ // Zvětší obsah b o jedna, tj. potom b je 11 a = b++ // a je rovno 10, b je rovno 11 a = ++b // a je rovno 11, b je rovno 11 b-- // Zmenší obsah b o jedna, tj potom b je 9 a = b-- // a je rovno 10, b je rovno 9 a = --b // a je rovno 9, b je rovno 9 Bitové posuny ( <<, >> ) Operátor posune bity celočíselné proměnné vlevo či vpravo o udaný počet míst a zbytek doplní nulami. Tedy například char a = 64; char b = a << 3; 64 8 char a = 133; char b = a >> 2; 133 20

  28. Podmínky Podmínky slouží pro rozhodování a větvení programu jejich syntax je následující: if (podmínka) příkaz 1; else příkaz 2 ; Část else je nepovinná. Tedy například if ( a >= 0 ) printf( “Cislo a je nezaporne“ ); else printf( “Cislo a je zaporne“ ); Jako podmínka může být libovolná booleanovská hodnota (nebo i celočíselná, kde 0 je považována za false, cokoliv jiného za true ). Na místě příkazu 1 a 2 nemusí být jen jediný příkaz, ale také příkazový blok : if ( a >= 0 ) { printf( “Cislo a je nezaporne \n“ ); printf( “Absolutni hodnota cisla a je %i \n“, cislo ); } else { printf( “Cislo a je zaporne“ ); printf( “Absolutni hodnota cisla a je %i \n“, - cislo ); } POZOR! Záměna operátoru == za operátor = v podmínce je zdrojem velmi častých a těžko objevitelných chyb: int a = 0; if( a = 2 ) printf( “a je dvojka“ ); vytiskne hlášení „a je dvojka“ i přes to, že v a očekáváme hodnotu 0! CVIČĚNÍ : výpočet kvadratické rovnice

  29. for cyklus For cyklus slouží pro opakované provádění příkazu nebo bloku příkazů. for( inicializace; test; akce ) příkaz; V částech inicializace, a akce může být teoreticky jakýkoliv platný příkaz nebo výraz, v části podmínka pak booleanovský výraz. Je-li tento výraz platný, cykl pokračuje, není-li cykl končí. Příkaz inicializace se provede jednou na začátku cyklu, příkaz akce se pak provede pokaždé na konci každého průběhu. Je obvyklé, že jednotlivé části se používají následovně: Inicializace …. deklarace řídící proměnné cyklu (celočíselná hodnota) Podmínka …… porovnání řídící proměnné s nějakou konstantou či předem známou hodnotou Akce …………. inkrementace či dekrementace řídící proměnné for ( intloop = 0; loop < 10; loop++ ) printf( “Ahoj! “ ); // Vytiskne 10x řetězec „Ahoj“ for ( intloop = 0; loop < 10; loop++ ) { // Vytiskne čísla od nuly do devíti a jejich druhé mocniny printf( “loop = %i \n“, loop ); printf( “loop ^2 = %i \n“, loop * loop ); } /*for( loop )*/

  30. while cyklus While cyklus slouží pro opakované provádění příkazu nebo bloku příkazů. while( podmínka ) příkaz; Slouží zejména v případě, kdy není zcela jasné, kolik průběhů bude cykl mít. Příkaz (nebo blok příkazů) se vykonává tak dlouho, dokud je podmínka platná. Může být použito například při načítání řádek ze souboru. Shcematicky : while ( ! soubor.JeNaKonci() ) { soubor.NactiData(); ZpracujData(); } Cyklus bude tak dlouho načítat a zpracovávat data, dokud nenarazí na konec souboru. Pak skončí. Funkční příklad zde: intcount; // Deklarace promene poctu opakovani printf( “Zadejte počet opakovani : “ ); scanf( “%i“, &count ); // Počet zadauzivatel z klavesnice while ( count > 0 ) // Cyklprobiha dokud je počet kladny { printf( “%i, “ ); // Tiskne se hlaseni o zbyvajicim poctu count--; // Počet se snizuje o jedna } printf( “ZAZEH!“ ); // Hlaseni o konci cyklu Pozn. : zadá-li uživatel nulu nebo záporné číslo, cykl neproběhne ani jednou!

  31. do-while cyklus Do-while cyklus slouží pro opakované provádění příkazu nebo bloku příkazů. do příkaz; while( podmínka ) ; Od samotného cyklu while se liší pouze v jediném – testovací podmínka je až na konci, takže tělo cyklu se minimálně jednou provede. char z = 'a'; do { printf( “%c“, z ); z++; } while( z <= 'z‚ ); // Vytiskne pismena od a do z . Pozn. : všimněte si, že znaková konstanta se dá použít na místě číselné hodnoty. V takovém případě překladač nahradí znak jeho ASCII hodnotou.

  32. Příkazy break a continue V souvislosti s cykly jsou zavedeny dva příkazy, a to break a continue. První z nich okamžitě ukončí provádění cyklu, druhý z nich ukončí provádění průběhu. Jejich použití a rozdíly jsou zřejmé z následujících příkladů: for ( intloop = 0; loop < 20; loop++ ) { // Vytiskne lichá čísla mezi nulou a dvacítkou if( (loop % 2) == 0 ) continue; printf( “%i, “, loop ); } /*for( loop )*/ intloop = 0; while( true ) { // Vytiskne lichá čísla mezi nulou a dvacítkou if( loop == 20 ) break; if( (loop % 2) == 0 ) continue; printf( “%i, “, loop ); loop++; } /*while*/ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, V obou případech bude stejný výstup: CVIČĚNÍ : výpočet faktoriálu resp. součet řady

  33. Výběrový příkaz switch-case Příkaz switch umožňuje větvení podle více hodnot celočíselných výrazů nebo výčtů. Výraz se vyhodnotí a jestliže odpovídá některé hodnotě case, skočí vykonávání na řádek, kde se case nachází. Jestliže neodpovídá ani jedna hodnota skočí na příkaz default (pokud je definován). Příaz(y) se vykonávají dokud nenarazí na break, po němž se přesune chod programu za složenou závorku. switch (výraz) { case konstanta 1: skupina příkazů 1; break; case konstanta 2 : skupina příkazů 2; break; . . . default: def. skupina příkazů break; } switch ( akce ) { case 1: printf( “Provadim akci 1 … \n“ ); break; case 2 : printf( “Provadim akci 2 … \n“ ); break; case 3 : printf( “Provadim akci 3 … \n“ ); break; default: printf( “Tuto akci neznam! \n“ ); break; } Zapomenete-li v nějaké sekci case napsat na konci break, provádění programu „proleze“ do další sekce. To lze v mnoha případech udělat i záměrně.

  34. Základy vstupu a výstupu intprintf(constchar *format, ... ) Funkce printf , implementována v knihovně stdio, zavádí standardní metodu pro formátovaný výstup na stdout. Může mít libovolný počet argumentů, první z nich je řetězec udávající formátování. Na tomto řetězci konkrétně záleží, kolik argumentů jakého typu musí funkce mít. char *jmeno = "Bobiku"; int vek = 21; printf( "Ahoj %s, jsi %d let stary (a bude hur).\n", jmeno, vek ); Ahoj Bobiku, jsi 21 let stary (a bude hur). Ve formátovacím řetězci jsou jednak normální znaky, jednak speciální sekvence, které jsou před tiskem nahrazeny obsahem proměnných. Pokud nesouhlasí počet a typ sekvencí s počtem a typem proměnných, výstup je nedefinovaný. Funkce vrátí celkový počet vytištěných znaků (nebo záporné číslo v případě chyby).

  35. Základy vstupu a výstupu Tabulka shrnuje nejčastěji používané formátovací znaky. Mezi procento a písmeno lze vložit číslo, které udává šířku výstupu : int vek = 21; printf( "Je mi %10u let.\n", vek ); printf( "Je mi %010u let.\n", vek ); Je mi 21 let. Je mi 0000000021 let.

  36. Základy vstupu a výstupu U reálných čísel lze přidat počet platných desetinných míst takto : intpi = 3.1415926535; printf( "pi = %f \n", pi ); printf( "pi = %8f \n", pi ); printf( "pi = %8.3f \n", pi ); pi = 3.141592653 pi = 3.141592 pi = 3.141 Počet znaků lze zadat záporný, pak se zarovnává doleva : char*jmeno = „Bobik"; printf( "Dnesni menu : %10s !\n", jmeno ); printf( "Dnesni menu : %-10s !\n", jmeno ); Dnesni menu : Bobik ! Dnesni menu : Bobik ! Počet mezer lze zadat i z programu pomocí znaku "*" : -> 1 -> 2 -> 3 for( int i = 1; i < 4; i++ ) printf( "->%*i \n", 2*i, i );

  37. Základy vstupu a výstupu intscan(constchar *format, ... ) Pro načítání hodnot je definována funkce scanf. Parametry má obdobné jako printf - řídící řetězec určuje konkrétní počet a typ proměnných, do kterých se bude načítat. Pokud řídící řetězec obsahuje cokoliv jiného než sekvence začínající "%", musí se tyto znaky objevit na vstupu (což je pro uživatele matoucí a není to doporučeno). int i; float f; double d;   printf( "Zadejte cele cislo: " ); scanf( "%d", &i );   printf( "Zadejte realnecislo: " ); scanf( "%f", &f );   printf( "Zadejte realnecislo s dvojitou presnosti: " ); scanf( "%lf", &d );   printf( "Zadal jste %d, %f, and %f\n", i, f, d ); Povšimněte si sekvence "%lf", která načítá reálné číslo s dvojitou přesností a také znaku "&" před jmény proměnných ve scanf. To jsou dereference, tj. funkci se předává nikoliv samotná proměnná, ale ukazatel na ni (o ukazatelích později) - parametr je předán odkazem. CVIČĚNÍ : výpočet různých řad závislých na parametru dle výběru uživatele

  38. Základy vstupu a výstupu intfprintf( FILE *output, constchar *format, ... ) intfscanf( FILE *input, constchar *format, ... ) Funkce printf a scanf operovaly se standardním výstupem a vstupem (stdout, stdin). Existují ale varianty těchto funkcí, které se chovají zcela shodně, výstup resp. vstup mají ale přesměrovaný na libovolný soubor. Jsou to funkce fprintf a fscanf. Aby bylo možné pracovat se souborem na disku, je třeba jej nejprve otevřít. K tomu slouží funkce fopen. FILE * fopen( constchar *fname, constchar *mode ); Fopen má dva argumenty. První z nich popisuje cestu k souboru na disku, druhý z nich pak režim, ve kterém bude soubor otevřen. POZOR! Pro MS Windows jsou v cestách k souborům zpětná lomítka a je tedy třeba je zadávat dvojitě: "C:\\Tmp\\soubor.txt"

  39. Základy vstupu a výstupu intfprintf( FILE *output, constchar *format, ... ) intfscanf( FILE *input, constchar *format, ... ) Funkce printf a scanf operovaly se standardním výstupem a vstupem (stdout, stdin ). Existují ale varianty těchto funkcí, které se chovají zcela shodně, výstup resp. vstup mají ale přesměrovaný na libovolný soubor. Jsou to funkce fprintf a fscanf. Aby bylo možné pracovat se souborem na disku, je třeba jej nejprve otevřít. K tomu slouží funkce fopen. FILE * fopen( constchar *fname, constchar *mode ); FILE je struktura definovaná v knihovně stdio popisující otevřený soubor. O její obsah se běžný programátor nemusí starat, pouze ukazatel na ni používá jako typ proměnné, která "obsahuje" soubor a předává ji jako argument všem příslušným funkcím. O ukazatelích později. Po ukončení práce se souborem je třeba jej zavřít : fclose( FILE * fileDescriptor); Bez zavolání tohoto příkazu se neuloží obsah bufferů a navíc bude soubor nadále zablokovaný pro jakýkoliv další proces!

  40. Základy vstupu a výstupu #include <stdio.h> intmain( void ) { char *polePrijmeni[] = { "Novak", "Jirousek", "Chudy", "Posedly" } ; char *poleJmen[] = { "Josef", "Jan", "Petr", "Maniak" } ; intpoleVeku[] = { 27, 35, 16, 1024 } ; // Defininice tri statickych poli s informacemi FILE *vystup = fopen( "seznam.txt", "w" ); // Vytvari se novy soubor pro vystup (pokud uz existuje, premaze se) fprintf( vystup, "JmenoPrijmeni Vek\n" ); fprintf( vystup, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" ); // Tiskne se hlavicka tabulky for( int i = 0; i < 4; i++ ) fprintf( vystup, "%10s %10s %5i \n", poleJmen[i], polePrijmeni[i], poleVeku[i]); // Tisknou se radky tabulky fclose( vystup ); // Vystupni soubor je zavren } /*main*/

  41. Základy vstupu a výstupu #include <stdio.h> intmain( void ) { charjmeno[100], prijmeni[100]; int vek; FILE *vstup = fopen( "seznam.txt", "r" ); // Otvira se soubor pro vstup printf( "JmenoPrijmeni Vek\n" ); printf( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" ); // Tiskne se hlavicka tabulky na obrazovku fscanf( vstup, "JmenoPrijmeni Vek\n" ); fscanf (vstup, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" ); // Preskocihlavicku v souboru while( ! feof( vstup ) ) { // Nacte se obsah souboru a vytiskne jako tabulka na obrazovku fscanf( vstup, "%s%s%i\n", jmeno, prijmeni, &vek ); printf( "%10s %10s %5i \n", jmeno, prijmeni, vek ); } /*while*/ fclose( vstup ); // Zavira se vstupni soubor } /*main*/ CVIČĚNÍ : zápis a načítání různých seznamů z/do souborů

  42. Funkce Funkce je část kódu, která se používá často a opakovaně na více místech. Bylo by nesmírně "neekonomické" opisovat kód znovu a znovu tam, kde je jej třeba. Proto je možné tuto část kódu zapouzdřit a nějak jej pojmenovat a ve zbytku programu už jen tímto jménem "volat". Obecná definice funkce vypadá následovně : typ jméno( parametr1, parametr2, ... ) { příkazy } #include <stdio.h> intsoucet( int a, int b ) { int r; r = a + b; return r; } /*soucet*/ voidmain ( void ) { int z; z = soucet( 5, 3 ); z = soucet( z, 2 ); printf( "5 + 3 + 2 = %i\n", z ); } /*main*/ typ ................ typ návratové hodnoty jméno ............ jméno funkce parametr# ..... hodnoty, které se do funkce předávají příkazy ........... samotný blok kódu Klíčové slovo return ukončuje provádění funkce a vrací řízení provádění programu těsně za volání funkce. Funkce nemusí vracet řádnou hodnotu, pak se na místo typu použije void a po klíčovém slovu return nebude uvedena žádná hodnota či proměnná. Stejně tak není nutné, aby měla nějaké vstupní parametry (rovněž se použije void).

  43. Funkce – argumenty předávané hodnotou Argumenty funkce jsou v jejím těle přístupné jako deklarované proměnné. Předávání může probíhat dvěma způsoby : hodnotou a odkazem. #include <stdio.h> intpracujSCisly( int a, int b ) { a++; b--; printf( "pracujSCisly : a = %i, b = %i\n", a, b ); return a + b; } /*pracujSCisly*/ voidmain ( void ) { int arg1 = 10; int arg2 = 15; printf( "arg1 = %i, arg2 = %i\n", arg1, arg2 ); int z = pracujSCisly( arg1, arg2 ); printf( "arg1 = %i, arg2 = %i, z = %i\n", arg1, arg2, z ); } /*main*/ Argument předaný hodnotou se v těle funkce chová jako nezávisle deklarovaná proměnná s hodnotou přiřazenou při deklaraci. Jakákoliv práce s ním neovlivní hodnoty proměnných v nadřazeném bloku, odkud byla funkce zavolána. arg1 = 10, arg2 = 15 pracujSCisly: a = 11, b = 14 arg1 = 10, arg2 = 15, z = 25

  44. Funkce – argumenty předávané odkazem Argumenty funkce jsou v jejím těle přístupné jako deklarované proměnné. Předávání může probíhat dvěma způsoby : hodnotou a odkazem. #include <stdio.h> intpracujSCisly( int& a, int& b ) { a++; b--; printf( "pracujSCisly : a = %i, b = %i\n", a, b ); return a + b; } /*pracujSCisly*/ voidmain ( void ) { int arg1 = 10; int arg2 = 15; printf( "arg1 = %i, arg2 = %i\n", arg1, arg2 ); int z = pracujSCisly( arg1, arg2 ); printf( "arg1 = %i, arg2 = %i, z = %i\n", arg1, arg2, z ); } /*main*/ Argument předaný odkazem naopak zůstává neustále svázán s proměnnou v nadřazeném bloku (předává se vlastně ukazatel). Argument předaný hodnotou se v definici funkce označuje znakem "&" za typem argumentu. arg1 = 10, arg2 = 15 pracujSCisly: a = 11, b = 14 arg1 = 11, arg2 = 14, z = 25

  45. Funkce – defaultové parametry Pro pohodlí programátora je možné nastavit defaultové hodnoty argumentů. Ty se pak při volání funkce vůbec nemusejí uvádět: [ 10, 10 ] [ 5, 1 ] [ -1, 1 ] #include <stdio.h> voidtiskniDvojiciCisel( intx = -1, inty = +1 ) { printf( "[ %i, %i ] ", x, y ); return; } /*tiskniDvojiciCisel*/ voidmain ( void ) { tiskniDvojiciCisel( 10, 10 ); tiskniDvojiciCisel( 5 ); tiskniDvojiciCisel(); } /*main*/ Defaultová hodnota se do definice funkce přidá pomocí rovnítka (jako přiřazovací příkaz). Hodnoty se do parametrů vkládají při překladu. Z tohoto důvodu je nutné, aby všechny parametry s defaultovými hodnotami byly až za parametry bez nich, v opačném případě by překladač nevěděl, co má dělat. povoleno : voidtiskniDvojiciCisel( intx = -1, inty = +1 ) {} povoleno : voidtiskniDvojiciCisel( intx, inty = +1 ) {} zakázáno : voidtiskniDvojiciCisel( intx = -1, inty ) {}

  46. Funkce - přetěžování Je možné definovat několik funkcí které mají stejné jméno a liší se jen argumenty. To je dobré v okamžiku, kdy mají tyto funkce provádět stejné věci pro různé typy argumentů. Například : První volání secti zavolá první z definovaných funkcí, druhé druhou a třetí poslední definovanou funkci. Přitom se všechny jmenují stejně a ze jména je jasné, že provádí aritmetickou operaci sčítání. Která funkce se kdy přesně volá rozhoduje překladač na základě typů argumentů. Pozn.: nelze deklarovat stejně se jmenující funkce se stejnými argumenty, lišící se pouze typem navrácené hodnoty. intsecti( intx, inty ) { return x + y; } double secti( double x, inty ) { return x + (double)y; } double secti( double x, double y ) { return x + y; } voidmain ( void ) { int a = 1; int b = 2; double c = 3.1415; double d = 2.7; intresInt; double resDoub; resInt = secti( a, b ); resDoub = secti( a, c ); resDoub = secti( c, d ) } /*main*/

  47. Funkce – rekurzivní volání Funkce může volat sama sebe. To je velice mocný nástroj pro řešení některého typu úloh - tzv. rekurzivní volání. Jako příklad takové úlohy může být vyhledávání souboru určitého jména v adresářové struktuře. Základní algoritmus takového vyhledávání zobrazuje vývojový diagram vlevo. Předpokládáme, že jméno hledaného souboru je známé na všech úrovních volání funkce. Argument funkce Prohlédni je adresář, ve kterém se má s hledáním začít. Její návratová hodnota je buď plná cesta k souboru, nebo prázdný řetězec (" ") v případě, že soubor nebyl nalezen. Prohlédni( adresář ) Načti seznam souborů ANO Obsahuje seznam hledaný soubor ? Vrať adresář + soubor NE Načti seznam podadresářů Cykl přes podadresáře (podadr) Volej Z = Prohlédni (adresář + podadr ) NE Vrať Z Z je rovno " " ANO Vrať " "

  48. Funkce – rekurzivní volání Jako příklad funkčního kódu je zde výpočet faktoriálu (byť pro tuto úlohu je rekurze zbytečná): #include <stdio.h> intSpoctiFaktorial( int n ) { if( n <= 1 ) return 1; return n * SpoctiFaktorial( n - 1 ); } voidmain ( void ) { int N; printf( "Zadejte cele cislo : " ); scanf( "%u", &N ); printf( "Faktorialcisla %u je %u. \n", N, SpoctiFaktorial( N ) ); } Všimněte si volání funkce SpoctiFaktorial primo z funkce printf - zde je jako argument printf použita návratová hodnota funkce SpoctiFaktorial.

  49. Složené typy – klíčové slovo typedef Je-li v programu třeba deklarovat více proměnných stejného složeného typu na různých místech, je nepraktické vypisovat typ znovu a znovu. Proto v C existuje klíčové slovo typedef, které překladači říká, aby si daný typ zapamatoval pod určeným jménem. Toto jméno je pak možné použít kdykoliv na deklaraci proměnné. typedef struct complex { float Re; float Im; }; complex Secti( complex clen1, complex clen 2 ) { complex soucet; soucet.Re = clen1.Re + clen2.Re; soucet.Im = clen1.Im + clen2.Im; return soucet; } void TiskniComplex ( complex cislo ) { if( cislo.Im < 0.0 ) printf( "%f - %fi", cislo.Re, abs( cislo.Im ) ); else printf( "%f + %fi", cislo.Re, cislo.Im ); } V příkladu je definován komplexní typ complex, který je pak v následujících funkcích použit jako typ argumentů a návratové hodnoty (o tom, jak se přesně volají funkce a o ostatních věcech použitých v příkladu později). complex A, B, C; A.Re = 10; A.Im = -3; B.Re = -5; B.Im = 5; C = Secti( A, B ); TiskniComplex( A); TiskniComplex( B ); TiskniComplex( C ); 10 - 3i -5 + 5i 5 + 2i CVIČĚNÍ : základní aritmetické operace se strukturou complex

  50. Ukazatele V C existuje speciální třída proměnných, které mají za úkol uschovávat odkazy na jiné proměnné. Říká se jim ukazatele. Ukazatele uschovávají nikoliv nějakou hodnotu, nýbrž odkaz do paměti (adresu), na níž lze patřičnou hodnotu nalézt. Schematicky : PI uchovává hodnotu 3.1415 3.1415 PI AF35:010B ukazatelNaPI uchovává adresu v paměti, na které je uložena hodnota PI AF35:010B ukazatelNaPI AF35:01BC Adresa proměnné Jméno proměnné Obsah proměnné Pomocí ukazatelů se v C předávají parametry odkazem, deklarují pole, alokuje paměť pro větší data, pracuje s řetězci a podobně. Některé jazyky tento mechanizmus programátorům neumožňují, protože je potenciálním zdrojem chyb a problémů, nicméně C se díky němu vyznačuje obrovskou flexibilitou. Ukazatel na proměnnou nějakého typu deklarujeme pomocí znaku * za typem: float PI; // Proměnná typu float float * ukazatelNaPI; // Proměnná typu ukazatel na float

More Related