Programování v jazyce C RNDr. Jan Lánský, Ph.D. Katedra softwarového inţenýrství MFF UK Katedra informatiky VŠFS (Autor původní verze slajdů: RNDr. Filip Zavoral, Ph.D.) zizelevak@gmail.com http://kocour.ms.mff.cuni.cz/~lansky/
Studijní povinnosti Zápočet aktivní účast na cvičeních během semestru 'domácí úkoly' (krátké prográmky) 2 povinné 2 dobrovolné (nejsou podmínkou zápočtu, ale lze je pouţít u zkoušky) Zkouška Musíte mě přesvědčit o vašich znalostech Ústní zkouška, diskuze nad zdrojovými kódy Zdrojové kódy musí být vlastní samostatnou tvorbou studenta Rozsah 1500 řádek na E, na lepší známky více IS studijní materiály PJC Priklady ke zkousce.doc 2
Obsah předmětu Programování v jazyce C ZS OOP (v C++) LS C C++ C C++ Nejdůleţitější: vlastní praxe Na přednáškách se nikdo nikdy programovat nenaučil 3
Přednáška Obsah přednášky Překlad programů, spojování Základní vlastnosti C a C++, odlišnosti od jiných prog. jazyků Datové typy, operátory a řídící konstrukce Pole a ukazatele Standardní knihovny Programování není zápis algoritmů Cvičení Praktické programování Microsoft Visual Studio.NET 2008 Ladění programů (!) Prosíím, já jsem napsal program a ono to řeklo 'Váš program provedl neplatnou instrukci a bude ukončen '. Co mám dělat? 4
Pascal vs. C++ Úvod do Programování, Programování heslo: programování = zápis algoritmů algoritmické myšlení, algoritmizace problému soustředění se na řešení problému formulace algoritmu a jeho zápis v nějakém formalismu (jazyku) základní datové a řídící struktury nedůleţité: kontrola vstupů, uţivatelské rozhraní, obedněnost, vazba na OS a HW, přenositelnost, optimalizace, udrţovatelnost výuka (v) Pascalu dobrý jazyk pro zápis algoritmů nezatěţuje technickými detaily (alokace paměti, vazba na OS,...) slabá podpora pro kontrolu vstupů, uţivatelské... 5
Pascal vs. C++ Programování v jazyce C++, OOP heslo: programování = vývoj software důleţité: kontrola vstupů, uţivatelské rozhraní, obedněnost, vazba na OS a HW, přenositelnost, optimalizace, udrţovatelnost zvládnutí knihoven a vývojových nástrojů 'Vše' jiţ bylo výuka (v) C++ naprogramováno standardní jazyk pro vývoj software další jazyky vycházejí z C++ (Java, C#,...) dobrá podpora pro kontrolu vstupů, uţivatelské... nutnost zvládnout technické detaily (alokace paměti, vazba na OS..) velké mnoţství hotového kódu (knihovny, komponenty,...) 6
Literatura Základní učebnice a popis jazyka Miroslav Virius: Programování v C++ (ČVUT, 2. vydání 2004) Miroslav Virius: Pasti a propasti jazyka C++ (Brno, 2. vydání 2005) Bjarne Stroustrup: The C++ Programming Language (3 rd ed.) Bruce Eckel: Myslíme v jazyku C++ (Thinkinkg in C++ 2 nd ed.) C++ In-depth aneb Jak správně C++ pouţívat - pro ty, kdo jiţ C++ nějak znají Scott Meyers: Effective C++ (2 nd ed.), More Effective C++ Herb Sutter: Exceptional C++, More Exceptional C++ Andrew Koenig, Barbara E. Moo: Accelerated C++ Practical Programming by Example Nicolai Josuttis: The C++ Standard Library A Tutorial and Reference James Coplien: Advanced C++ Programming Styles and Idioms Que: ANSI/ISO C++ Professional Programmer's Handbook Andrei Alexandrescu: Modern C++ Design Generic Programming and Design Patterns Applied Normy ISO/IEC 9899: Programming languages - C (1999) ISO/IEC 14882, ANSI: Programming languages - C++ (1998, 2003) 7
Nevhodná literatura - nepoužívat! Martin Beran: Učebnice Borland C++ - hrubé chyby Jan Pokorný: Rukověť uţivatele Borland C++ - staré, BC 3.1 Vladimír Rudolf: ABC programátora v C++ - neúplné, zastaralé Dirk Louis: C und C++ Programierung und Referenz - chyby Dalibor Kačmář: Jazyk C učebnice pro střední školy chyby Brodský, Skočovský: Operační systém Unix a jazyk C neúplné, zastaralé Eric Gunnerson: Začínáme programovat v C# C# není C++ 8
Historie 1970-73 první verze C, společný vývoj s UNIXem 1973 přepsání jádra UNIXu do C 1978 Kerninghan, Ritchie: The C Programming Language 1980 standardy ANSI X3J11, od r. 1999 ISO 9899 1980 AT&T - "C with Classes" 1983 poprvé název C++ (Rick Mascitti) 1985 Stroustrup: The C++ Programming Language 1989 ANSI X3J16 norma C++ 2003 nejnovější ISO/ANSI norma C++ Normy se vyvíjí, aktuální překladače o několik let zpět Implementace novinek často nekorektní nebo neefektivní (STL) 9
hello.c #include <stdio.h> #include <conio.h> int main( int argc, char ** argv) { printf( "Hello\n"); getch(); return 0; 10
hello.c direktiva preprocesoru vloţení souboru deklarace knihovních funkcí název funkce #include <stdio.h> #include <conio.h> hlavička funkce typ návratové hodnoty tělo funkce příkaz sloţené závorky BEGIN END int main( int argc, char ** argv) { printf( "Hello\n"); getch(); return 0; skutečné parametry volání funkce bez parametrů formální parametry 11
Struktura programu Program se skládá z modulů Překládány samostatně kompilátorem Spojovány linkerem Modul z pohledu programátora Soubor s příponou.cpp (.c) Hlavičkové soubory Soubory s příponou.h Deklarují (a někdy i definují) identifikátory pouţívané ve více modulech Vkládány do modulů direktivou include Direktivu zpracovává preprocesor čistě textově Preprocesor je integrován v kompilátoru jako první fáze překladu Modul z pohledu kompilátoru Samostatná jednotka překladu Výsledek práce preprocesoru 12
Překlad jednoho modulu a sestavení knihovní headery knihovny standardní i jiné.h.obj.obj.obj.obj.obj.lib spustitelný program.cpp CC.obj Link.exe kompilace objektový modul (přeloţený kód) spojování (linkování) 13
Oddělený překlad - dělení na moduly Modul - ucelená funkčí jednotka modul.cpp (.c) - implementace modul.h - definice rozhraní rozdělení projektu do modulů a vytváření headerů je umění, nedá se to naučit na přednášce fotbal.h hriste.h hrac.h mic.h fotbal.cpp hriste.cpp hrac.cpp mic.cpp 14
Překlad více modulů oddělený překlad vlastní headery knihovní headery knihovny.h.h.obj.obj.obj.obj.obj.lib.c CC.obj Link.exe kompilace jednoho modulu.c.c.c.obj.obj.obj další moduly 15
Překladače a vývojová prostředí Windows - překladač součástí integrovaného prostředí MS Visual Studio - Visual C++ (VC6.0,.Net,.Net2008) integrovaný make, linker, debugger raději nepouţívat klikabilní konfigurace další překladače - Borland C++ Builder, Intel, Watcom Unix (Linux) - samostatné programy, příkazová řádka gcc make - pro 'opravdové' programátory pokusy o vývojová prostředí (kdevelop) 16
Integrované vývojové prostředí.h.h.obj.obj.obj.obj.obj.lib.c.c.c.obj CC.obj.obj Link.exe editor projekt debugger 17
Make.h.h.obj.obj.obj.obj.obj.lib.c.c.c.obj CC.obj.obj Link.exe makefile make 18
Program a modul Program v C++ nemá (vnořovanou) blokovou strukturu Neexistuje "hlavní blok" Běh programu začíná vyvoláním funkce main Před funkcí main běţí inicializace běhového prostředí, po ní úklid Funkce main musí mít tuto hlavičku: int main( parametry příkazové řádky) Modul: posloupnost globálních deklarací a definic Deklarace Oznámení překladači, ţe identifikátor existuje Určení některých vlastností (typ, parametry) Definice Doplnění zbývajících vlastností (kód funkce, obsah struktury) Překladač vygeneruje kód funkce, vyhradí místo pro proměnnou 19
Funkce Základní programová jednotka je funkce Neexistují vnořené funkce Začátek programu funkce main hlavička funkce tělo funkce int fce1( int x, int y) { return x+y; int fce2( int a) { return fce1( 1, 2*a+5); int fce2( int a) { int fce1( int x, int y) { return x+y; vnořené funkce nelze! return fce1( 2*a+17); začátek programu int main( int argc, char** argv) {... argumenty z příkazové řádky později 20
Funkce - návratový typ Typ funkce = typ návratové hodnoty Hodnota se vrací pomocí klíčového slova return Speciální typ void - 'prázdný' typ ekvivalent procedury Pascalu typ funkce int fce1( int x, int y) { return x+y; návrat celočíselné hodnoty void fce2( char* text) { printf( text); void fce3( char* text) { if(! text) return; printf( text); návrat z funkce 21
Parametry funkce Pevný počet, pevný typ moţnost proměnného počtu parametrů - printf Parametry jsou odděleny čárkou U kaţdého parametru musí být uveden jeho typ Funkce bez parametrů - void kaţdý parametr musí mít typ int fce1( int x, int y, float z) {... int fce2( double a, char* str) {... int fce3( void) {... int fce4( int x, y) {... int fce5 {... 22
Volání funkce Shoda počtu formálních a skutečných parametrů Kompatibilita typů formálních a skutečných parametrů I funkce bez parametrů musí obsahovat operátor () Návratová hodnota - lze ignorovat int fce1( int x, int y, float z) {... int fce3( void) {... int fce2( int a) { fce1( a, 1, a); fce3(); return 0; int fce4( int x, int y, float z) {... int fce5( int a) { fce4; fce4( a, 1); fce4( a, 1, "ahoj"); 23
Lokální proměnné Definice lokálních proměnných C: na začátku těla funkce (přesněji: na začátku bloku) C++: kdekoliv v těle funkce Moţná inicializace při kaţdém běhu funkce neinicializovaná proměnná náhodná hodnota!!! int fce( void) { int x, y; int z = 0; return z + x; deklarace celočíselných proměnných deklarace s inicializací náhodná hodnota!!! 24
Příklad - malá násobilka definice knihovních funkcí inicializovaná celočíselná proměnná kontrola parametrů začátek cyklu konec cyklu hlavní program ignorování návratové hodnoty The END #include <stdio.h> int vynasob( int c) { int i = 1, v; if( c < 1 c > 10) return -1; while( i <= 10) { v = i * c; printf( "%d * %d = %d\n", i, c, v); i = i + 1; return 0; int main() { int cislo = 7; vynasob( cislo); return 0; hlavička funkce, formální parametr neinicializovaná proměnná okanţitý návrat z funkce 2 * 7 = 14 i++ konec, OK konec, OK volání funkce se skutečným parametrem 25
Předávání parametrů hodnotou Všechny parametry se předávají hodnotou 'Výstupní' parametry pouze přes ukazatele nebo reference void swap( int x, int y) { int pom; pom = x; x = y; y = pom; void fce( void) { int a = 1; int b = 2; int c = 0; swap( a, b); c = 2 * a + b; vymění se jen lokální proměnné funkce předají se pouze hodnoty (1, 2), nikoliv 'proměnné' 26
Globální proměnné Definice mimo tělo funkce Viditelné v jakékoliv funkci Moţná inicializace při startu programu Pouţívat s rozvahou!! pouze pro sdílená data lokální proměnná globální proměnná int g = 1; void fce( int x) { int z = 2; g = g + z * x; formální parametr co vrátí main?? int main( void) { fce( 2); fce( 3); return g; skutečný parametr 27
Výraz Norma: "Výraz je posloupnost operací a operátorů specifikující výpočet hodnoty" Přiřazovací 'příkaz' je také výraz jeho hodnotou je přiřazovaná hodnota Výrazy mohou mít vedlejší efekty 1 a+b*sin(x) printf( "Ahoj") q = &b[17]+*p++ "retezec" a = b = 0; if( (x = fnc())!= 0)... uţitečné: test přiřazované hodnoty 28
Příkaz Příkaz je výraz ukončený ';' (středníkem) sloţený příkaz - posloupnost příkazů mezi '{' a '' programová konstrukce if, if-else, switch, while, do-while, for, break, continue, return, goto 1; a+b*sin(x); printf( "Ahoj"); q = &b[17]+*p++; "retezec"; { 1; a+b*sin(x); printf( "Ahoj"); q = &b[17]+*p++; "retezec"; jeden sloţený příkaz 29
Podmíněný příkaz if (výraz) příkaz if (výraz) příkaz else příkaz if( a > 1) printf( "OK"); if( a > 1) printf( "OK"); else printf( "nee"); syntakticky špatně syntakticky správně, ale dělá něco jiného if( a > 1) b = 0; printf( "OK"); if( a > 1) b = 0; printf( "OK"); else printf( "nee"); if( a > 1) { b = 0; printf( "OK"); else { printf( "nee"); if( a > 1) { b = 0; printf( "OK"); else { printf( "nee"); 30
Vnořené podmíněné příkazy Syntakticky správně, ale nepřehledné Na pohled nejasné párování if( a > 1) if( b > 0) printf( "OK"); else printf( "nee"); U vnořených podmínek vţdy pouţívat { if( a > 1) { if( b > 0) printf( "OK"); else { printf( "nee"); if( a > 1) { if( b > 0) printf( "OK"); else printf( "nee"); 31
Vícenásobné větvení konstrukce switch celočíselný výraz switch (výraz) { case konstanta: posloupnost příkazů break; case konstanta: case konstanta: posloupnost příkazů break; default: posloupnost příkazů pokud výraz není roven ţádné z konstant ukončení větve switch( errc) { case 0: b = 0; printf( "OK"); break; case -1: printf( "trochu spatne"); break; case -2: case -3: printf( "hodne spatne"); break; default: printf( "jina chyba"); break; interval nelze zapomenutý break syntakticky OK, ale dělá něco jiného switch( errc) { case 0: b = 0; printf( "OK"); case -1: printf( "spatne"); break; switch( ch) { case '0'..'9': x += ch - '0'; break; case 'A'..'F': x += ch - 'A'; break; 32
Cyklus konstrukce while a do-while while (výraz) příkaz do příkaz while (výraz) ; podmínka na začátku podmínka na konci Pozor! cyklus pokračuje pokud je podmínka platná while( a > 1) a = a / 2; tělo se vţdy alespoň jednou provede while( a > 1) { fce( a); a = a / 2; do { fce( a); a = a / 2; while( a > 1); 33
Cyklus konstrukce for inicializace podmínka inkrement tělo cyklu for (výraz1 ; výraz2 ; výraz3 ) příkaz je ekvivalentem výraz1; while (výraz2 ) { příkaz výraz3 ; jako inicializaci, podmínku i inkrement lze libovolný výraz FOR I := 0 TO 9 DO FCE(I) ekvivalent v jiném jazyce for( i=0; i<=9; i=i+1) { fce( i); i=0; while( i<=9) { fce( i); i=i+1; for( init(a); i<9 && a[i] >0; a[i++]=i*2) { fce( i); for v C++: obecnější, širší moţnosti pouţití 34
Ukončení cyklu - break, continue break okamţité ukončení celého cyklu cyklus s podmínkou uprostřed ošetření chybových stavů continue ukončení (jednoho) běhu těla cyklu for(;;) { errc = fce(); if( errc < 0) break; jinafce(); n = 1; while( n<1000) { val = get(); if( val == 0) continue; n = n * val; 35
Goto Nepoužívat!... pokud k tomu není dobrý důvod Kdyţ uţ, tak pouze dopředu (skok dozadu = cyklus) Dobrý důvod: výskok z vícenásobně vnořených cyklů label návěští for( i=0; i<10; i++) { for( j=0; j<10; j++) { if( fnc( i, j) == ERROR) goto konec_obou_cyklu; konec_obou_cyklu: dalsi_funkce(); nelze break - ukončil by pouze vnitřní cyklus, vnější cyklus by pokračoval 36
Celočíselné typy Základní celočíselné typy jsou znaménkové Pro kaţdý typ existuje unsigned varianta moţné vyuţití unsigned: unsigned char, pole bitů, modulová aritmetika pokud není dobrý důvod, unsigned raději nepouţívat char short int long long long size_t, ptrdiff_t, wchar_t typ 8 bit 16 bit 32 bit 64 bit char 8 8 8 8 short 8 / 16 16 16 16 / 32 int 16 16 32 32 long 16 32 32 32 / 64 long long - - 64 64-2GB.. +2GB 37
Logické a znakové hodnoty a typy C: Aţ do r. 1999: neexistuje typ 'boolean' Porovnání a logické operátory jsou celočíselné výrazy FALSE (nepravda) 0 TRUE (pravda) 1 (libovolná hodnota různá od 0) důsledek: if( x!= 0) if( x) if( x == 0) if(!x) C++, C99 celočíselný typ bool (C99: _Bool) hodnoty true (=1), false (=0) char časté pouţití: test (ne)nulovosti např. ukazatelů norma neurčuje signed / unsigned korektní porovnání na nerovnost pouze 0.. 127 'a' < 'ţ'? záleţí na implementaci většinou char = signed signed char -128.. 127 40 > 200!!! unsigned char 0.. 255 - 'byte' 200-56 wchar_t stddef.h: znak rozšířené sady (Unicode) 38
Výčtový typ enum pohlavi { p_muz, p_zena ; pohlavi p = p_muz; int pp = p_zena; pohlavi q = 0; C: celočíselné konstanty - OK C++: samostatný typ - nelze hodnoty doplní překladač (od 0) enum flags { f1 = 1, f2 = 2, f3 = 4, f4 = 8 ; if( x & f1)... test bitů explicitní hodnoty enum porty { pop3 = 111, ftp = 21, smtp = 80 ; 39
'Reálné' typy float double long double Pozor! Reálné výpočty jsou vţdy nepřesné malá přesnost - nepouţívat! zvýšená přesnost standard pro 'reálné' výpočty double x = 1; double y = x / 3; if( x == 3 * y) printf( "Presne"); else printf( "Nepresne"); raději nepouţívat pouze pro fyzikální nebo numerické veličiny double zustatek = 15.60; long zustatek_v_halerich = 1560; pro přesné hodnoty pouţívejte přesné typy 40
Celočíselné konverze Automatické konverze (integral promotions) Výpočty výrazů a předávání parametrů vţdy v šíři alespoň int signed char, unsigned char, signed short signed int unsigned short signed int (pokud je int delší) / unsigned int vţdy kdyţ je pouţit menší typ neţ int Automatické konverze u binárních operací signed int unsigned int signed long unsigned long float double long double při nestejných operandech 41
Přehled operátorů, asociativita a priorita postfix prefix ++ -- ( ) [ ] ->. :: ++ --! ~ + - & * sizeof ( ) new delete post-in/de-krementace volání funkce index přístup k poloţce struktury kvalifikace identifikátoru pre-in/de-krementace booleovská a bitová negace unární +/- reference, dereference měření velikosti přetypování dynamická alokace dynamická dealokace L.* ->* dereference member-ptru L * / % multiplikativní operátory L + - aditivní operátory L << >> bitové posuny L < <= > >= L ==!= L & L ^ L L && L L? : P = *= /= %= += -= &= ^= = <<= >>= L, uspořádání rovnosti bitové AND bitové XOR bitové OR booleovské AND booleovské OR podmíněný výraz přiřazení kombinované přiřazení sekvence 42
Základní aritmetické operátory + - * / % podle typu operandů automatická konverze na větší typ % - modulo int x=5, y=3; celočíselné dělení reálné dělení double a=5, b=3; x / y 1 x / b 1.666 x % y 2 x % b Error a / b 1.666 a / y 1.666 modulo je pouze celočíselná operace a % b Error a % y Error 43
Bitové a logické operátory & ^ ~ - bitové operace AND, OR, XOR, NOT &&! - logické operace AND, OR, NOT 5 = 0101 2 3 = 0011 2 1 = 0001 2 7 = 0111 2 9 = 1001 2 15 = 1111 2 10 = 1010 2 5 & 3 1 5 && 3 1 5 3 7 5 3 1 5 ^ 3 6 5 ^^ 3 Error 5 ^ 15 10 5 & 0 0 5 && 0 0 5 0 5 5 0 1 oba op. 0 alespoň jeden operand 0 neexistuje alespoň jeden operand = 0 44
Zkrácené vyhodnocování, relační operátory a && b - je-li a=0, b se nevyhodnocuje, výsledek = false (0) a b - je-li a=1, b se nevyhodnocuje, výsledek = true (1) int x[10]; // pole 0..9 if( i < 10 && x[i]!= 0) y = y / x[i]; test mezí pole před přístupem k prvku pole if( x==y && *x++)... Pozor! operátory s vedlejšími efekty se nemusí provést! < <= >= > ==!= výraz typu int (bool) - výsledek vţdy 0 nebo 1 (false, true) porovnávání na (ne)rovnost float/double! porovnání vs. přiřazení! POZOR!!! Přiřazení! (zde hodnota vţdy = 1) if( x = 1)... 45
Přiřazení, inkrementace, bitový posun = += -= *= /= %= &= = ^= <<= >>= kombinované přiřazení a op= b a = a op b i += 2; x[ i+=1] /= 3; ++ -- a++ a = a + 1, výsledkem je stará hodnota a ++a a = a + 1, výsledkem je nová hodnota a přesněji: a++ (tmp = a, a = a + 1, tmp) int sum = 0; int i, x[10];... for( i=0; i<9; sum += x[i++]) ; << >> bitový posun C++ - časté pouţití pro jiné účely (streams) - přetěţování pozor - vţdy si uvědomit, zda jde o pre- nebo post- inkrementaci 46
Podmíněný výraz, sekvence a? b : c ternární operátor po vyhodnocení podmínky a se vyhodnotí buď b (je-li a!= 0) nebo c (je-li a == 0) x = (y>0? y : 0); a, b operátor sekvence ('zapomnění') po úplném vyhodnocení a se vyhodnotí b x = (tmp = y, y = y + 1, tmp); ekvivalent x = y++; 47
Pravidla vyhodnocování a( b,c) vedlejší efekty parametrů jsou vyhodnoceny před zavoláním fce a && b je-li a nulový, b se nevyhodnocuje a b je-li a nenulový, b se nevyhodnocuje a? b : c po vyhodnocení a se vyhodnotí buď b nebo c a, b po úplném vyhodnocení a se vyhodnotí b Ţádná další pravidla nejsou ostatní operátory jsou vyhodnocovány v libovolném pořadí vedlejší efekty se mohou projevit kdykoliv během výpočtu i = 0; p[ i++] = i++; moţné výsledky: p[0] = 0; p[1] = 0; p[0] = 1; 48
Fungující triky vs. chyby Fungující triky Test ukazatele while ( p && p->v < v ) p = p->next; Volání funkce s vedlejším efektem while ( (c = getchar())!= EOF && c!= '\n' ); Kopie řetězce while ( *a++ = *b++ ); Chyby Vícenásobný výskyt modifikované proměnné p[ i++] = i++; nevím, jestli se provede Nadbytečné volání funkce s vedlejším efektem if ( getchar() == 'A' && getchar() == 'B' ) 49
Pole Indexy polí vţdy začínají od 0! Při přístupu se nikdy nekontrolují meze!!! Deklarace: t x[n] - pole x o n prvcích (0..n-1) typu t Přepis náhodného místa v paměti! Nepředvídatelné následky!! int x[5]; for( i=1; i <=5; i++) x[i] = i; 0 1 2 3 4???? 1 2 3 4 5 Vícerozměrné pole je pole polí Deklarace: t x[m][n]; Přístup: x[m][n] = a; význam: m-prvkové pole typu (n-prvkové pole typu t) int x[8][8]; for( i=0; i < 8; i++) for( j=0; j < 8; j++) x[ i ] [ j ] = i * j; Pozor! x[m,n] není prvek dvojrozměrného pole o souřadnicích m a n čárka = operátor sekvence 50
Inicializace pole Při deklaraci pole lze obsah pole inicializovat Nejde o přiřazení, lze pouze při deklaraci Deklarace inicializovaného pole nemusí obsahovat velikost překladač dopočítá z inicializace u vícerozměrných polí pouze "vnější" rozměr Při nesouhlasu velkosti pole a počtu inicializátorů velikost > inicializátory: inicializace začátku, obsah zbytku nedefinován velikost < inicializátory: kompilační chyba (Too many initializers) int cisla[] = { 1, 2, 3, 4 ; char* jmena[] = { "Josef", "Karel", "Jana" ; int matice[][3] = { { 11, 12, 13, { 21, 22, 23 ; 51
Ukazatele Co to je proměnná? místo v paměti, typ (-> velikost), hodnota hodnota se dá číst a většinou i zapisovat je nějak velká (typ) někde bydlí 1 má hodnotu Co to je ukazatel (pointer)? něco, čím si můţu ukazovat na proměnnou (nebo na jiné paměťové místo pole, poloţka struktury, dynamická alokace) K čemu jsou ukazatele dobré: zpracování řetězců, dynamické datové struktury, předávání parametrů odkazem, práce s buffery,... Pro C++ je práce s ukazateli typická 52
Ukazatele Deklarace: t x proměnná x typu t t *p - ukazatel p na typ t p: Operátor reference: p = &x Operátor dereference: x = *p Neinicializovaný ukazatel vs. nulový ukazatel C: #define NULL 0 C++: 0 x: 17 53
Ukazatele - příklad int x = 1, y = 3; int * px, * py; *px = 5; py = NULL; *py = 7; if(! py) etwas(); px = &x; py = &y; (*py)++; px = py; y = x; py = &x; *py = 9; přepsání náhodného místa v paměti? 5 x: 1 :px y: 3 :py? jaký zde bude obsah x a y? 54
Ukazatele - příklad int x = 1, y = 3; int * px, * py; *px = 5; py = NULL; *py = 7; if(! py) etwas(); px = &x; py = &y; (*py)++; px = py; y = x; py = &x; *py = 9; umoţní test x: 1 :px y: 3 :py 0: 7 typicky 'segmentation fault' váš program bude ukončen pro pokus o porušení ochrany paměti 55
Ukazatele - příklad int x = 1, y = 3; int * px, * py; *px = 5; py = NULL; *py = 7; if(! py) etwas(); px = &x; py = &y; (*py)++; px = py; y = x; py = &x; *py = 9; Pozor na prioritu! *py++ *(py++) x: 1 :px y: 3 :py x: 1 :px y: 4 :py přístup k hodnotě proměnné přes ukazatel 56
Ukazatele - příklad int x = 1, y = 3; int * px, * py; *px = 5; py = NULL; *py = 7; if(! py) etwas(); px = &x; py = &y; (*py)++; px = py; y = x; py = &x; *py = 9; x: 1 :px y: 1 :py x: 9 :px jaký zde bude obsah x a y? y: 1 :py 57
Pole a ukazatele, aritmetika ukazatelů int a[5]; int *p; a[2] = 20; p = &a[2]; a[0] = *p - 15; p++; *p = 30; a 0 1 2 3 4?? 20?? p reference na prvek pole 5? 20 30? p inkrementace ukazatele posun na daší prvek 58
Pole a ukazatele, aritmetika ukazatelů p = &a[1]; *(p+3) = 40; 5? 20 30 40 p přičtení čísla k ukazateli posun o n prvků Operátor [] p[i] *(p+i) &p[i] p+i a &a[0] indexování pole (ukazatele) je jen jiný zápis přístupu přes ukazatel identifikátor pole je ukazatel na svůj nultý prvek Automatické konverze pole-ukazatel Je-li výraz typu pole na místě, kde typ pole nemůţe být, automaticky se konvertuje na ukazatel na nultý prvek tohoto pole. Pole nelze přiřazovat ani předávat hodnotou (ukazatel na nultý prvek) p = a je ekvivalentní p = &a[0] 59
Pole a ukazatele, aritmetika ukazatelů int a[5]; int *p; 0 10 20 30 40 p = &a[0]; p = a; *p = a[1]; *(p+2) = a[2] 1; p = a + 2; p[1] = *(a+1); a[3] = p[2]; *(a+2) = p[-1]; 3[a] = 2[p]; nekompatibilní typy nestejná úroveň indirekce a[4] = p + 1; p = a[0]; p[1] = a; a = p + 2; identifikátor pole je konstantní nelze do něj přiřazovat p[i] *(p+i) *(i+p) i[p] 60
Řetězce Jazyk C++ nezná pojem řetězec (!) konvence, knihovny Řetězec je pole znaků (char) zakončené nulou Kaţdý řetězec musí být vždy ukončen nulou "Ahoj" 'A' 'h' 'o' 'j' '\0' char buffer[4]; strcpy( buffer, "Ahoj"); 'A' 'h' 'o' 'j' '\0' '\0' = 0 pozor na uvozovky a apostrofy! vţdy myslet na koncovou nulu! X 'X' "X" proměnná znaková konstanta - celočíselná hodnota řetězec - ukazatel kdyţ chcete říct mezera, napište mezeru (' ') ať vás ani nenapadne napsat 32 přestoţe to na VAŠEM počítačí funguje kód znaku v pouţitém kódování (ASCII, CP1250, ISO08859-2, EBCDIC,...) 61
'Řetězcové proměnné' Inicializované pole (konstantní ukazatel) s1++ nelze! char s1[] = "Uno"; const char *s2 = "Due"; s1: s2: 'U' 'n' 'o' '\0' Inicializovaná proměnná typu ukazatel s2++ se přesune na další znak 'D' 'u' 'e' '\0' anonymní globální proměnná const char[] 62
Řetězce knihovní funkce, délka řetězce v C neexistují 'řetězcové operace' (C++: třída string) přiřazení, zřetězení, porovnání, podřetězec,... vše standardní knihovní funkce #include <string.h> int strlen( const char* s); deklarace řetězcových funkcí počet znaků (bez koncové nuly) char pole[8] = "Ahoj"; x = strlen( pole); // 4 inicializované pole typu char skutečný počet znaků (4) nikoliv velikost pole pole: A h o j \0??? 63
Délka řetězce různé způsoby implementace přístup přes index podmínka for cyklu můţe být libovolná int strlen ( const char* s) { int i = 0; while ( s[i]!= '\0') { i++; return i; int i = 0; while ( *s!= '\0') { i++; s++; return i; přístup přes ukazatel více inkrementací prázdné tělo nezapomenout na ';'!! sloţitější podmínka: test nenulovosti inkrementace ukazatele while(a!=0) while(a) podmínka je splněna pokud je nenulová rozdíl ukazatelů = počet prvků mezi nimi pozor na ± 1! for( i=0; *s!= '\0'; i++) s++; for( i=0; *s!= '\0'; i++, s++) ; int i=0; while ( *s++!= '\0') i++; int i=0; while ( *s++) i++; char *p = s; while (*p++) ; return p-s-1; 64
Řetězce - kopírování char* strcpy( char* d, const char* s); char buf[8]; char pozdrav[] = "Ahoj"; strcpy( buf, pozdrav); zkopíruje obsah s do místa začínajího od d kopíruje pouze do koncové '\0' buf pozdrav A h o j \0??? A h o j \0 65
Řetězce chyby při kopírování char buf[6]; char pozdrav[] = "Dobry den"; strcpy( buf, pozdrav); vţdy pozor na dostatek místa funkce nic nekontroluje!!! váš program provedl... buf pozdrav D o b r y d e n \0 D o b r y d e n \0 char *ptr; char pozdrav[] = "Ahoj"; strcpy( ptr, pozdrav); ptr neinicializovaný!!! kopírování na neinicializovaný ukazatel!!! váš program provedl... ptr pozdrav? A h o j \0 A h o j \0 66
Řetězce zřetězení, vyhledávání char* strcat( char* d, const char* s); char* strchr( const char* s1, int c); char* strstr( const char* s1, const char* s2); připojí s za d vyhledá první pozici c v s1 vyhledá podřetězec s2 v s1 char buf[10]; char* bp; strcpy( buf, "Ahoj"); strcat( buf, "Babi"); bp = strstr( buf, "oj"); po strcpy po strcat A h o j \0????? A h o j B a b i \0? pozor na dostatek místa! buf A h o j B a b i \0? bp 67
Řetězce porovnávání a další funkce int strcmp( const char* s1, const char* s2); s1 < s2-1 s1 = s2 0 co znamená 's1 < s2'? s1 > s2 +1 lexikografické uspořádání (jako ve slovníku) výsledek podle prvního rozdílného znaku A B C E = = = A B C D E další řetězcové funkce: strncat, strncmp, strncpy strrchr Find last occurrence of given character in string strpbrk Find first occurrence of character from one string in another string strspn Find first substring from one string in another string strtok Find next token in string sprintf Write formatted data to a string 68
Funkce printf int printf( const char *format [, argument]... ) Vytiskne na standardní výstup text definovaný formátovacím řetězcem Formátovací řetězec určuje počet a typy dalších parametrů (symbolem %) Výjimka z pravidla, ţe funkce v C má přesně daný počet a typy parametrů. Kompilátor nemá moţnost ověřit zda parametry jsou správné časté pády programu Vrací počet skutečně vytisknutých znaků %s řetězec %d, %i celé číslo %c znak %f reálné číslo %08d zarovná na 8 míst, doplní 0 %5.2f zarovná na 5 míst, 2 desetinná místa \n odřádkuje \t tabulátor \\ \ (cesta k souborům) #include <stdio.h> double r = 2.6; int c = 65; char * buf = "Ahoj"; printf ("Soucet cisel %d + %.2f = %.2f \n", 7, r, r + 7); printf ("Retezec %s zacina na %c \n", buf, buf[0]); printf ("ASCII kód znaku %c je %i \n", c, c); printf ("%s", r); printf ("%i + %i = %i", c, c); 69
Parametry příkazové řádky C:\> myprog.exe -n -w a.txt b.txt int main( int argc, char** argv) pole řetězců (ukazatelů na char) argv 0 argc 5 b. t x t \0 Počet parametrů včetně názvu programu! = počet ukazatelů v argv - w \0 a. t x t \0 - n \0 m y p r o g. e x e \0 70
Zpracování příkazové řádky výpis parametrů C:\> myprog.exe -n -w a.txt b.txt int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; myprog.exe -n -w a.txt b.txt posun na další parametr výstup řetězce a odřádkování 71
Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; argv[4] argv[4][1] *argv argv[0] typ: char* argv typ: char** 0 b. t x t \0 a. t x t \0 - w \0 - n \0 argv m y p r o g. e x e \0 **argv argv[0][0] typ: char 72
Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; 0 b. t x t \0 argv++ a. t x t \0 - w \0 - n \0 **argv argv m y p r o g. e x e \0 73
Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; 0 b. t x t \0 *argv == 0 a. t x t \0 argv - w \0 - n \0 m y p r o g. e x e \0 74
Zpracování příkazové řádky usage: myprog [-n] [-w] filea fileb int main( int argc, char** argv) { int n=0, w=0; while( *++argv && **argv=='-') { switch( argv[0][1]) { case 'n': n = 1; break; case 'w': w = 1; break; default: error(); if(!argv[0]!argv[1]) error(); doit( argv[0], argv[1], n, w); return 0; options nastavení přepínače zbývající parametry 0 b. t x t \0 a. t x t \0 výkonná funkce argv - w \0 - n \0 p r g. e x e 75 \0
Dynamická alokace paměti C: standardní knihovny <malloc.h> void* malloc( int size); void free( void* p); Velikost datového typu sizeof(char) = 1 vţdy C++: součást jazyka (podrobně později) new T new T[ n] delete p delete[] p int * pole; pole = malloc(20 * sizeof(int)); C++ nutnost přetypování s = (char*) malloc( 20); lépe new char* s; s = malloc( 20); if(! s) error(); strcpy( s, "ahoj"); *s = 'X'; vţdy ověřit!!! váš program provedl... s: X h o j \0 76
Velikost pole určena za běhu programu int main( int argc, char** argv) { char* buf;... buf = malloc( strlen(argv[1]) + strlen(argv[2]) + 1))); if(! buf) error(); strcpy( buf, argv[1]); strcat( buf, argv[2]); spočítá potřebnou velikost ze vstupních parametrů pozor na koncovou '\0' 77
Organizace paměti procesu IP R0 R1... SP Kódový segment Kód programu Datový segment Heap Globální proměnné Dynamicky alokovaná data Zásobník Lokální proměnné a parametry funkcí 78
Organizace paměti procesu kódový segment IP R0 R1... SP Kódový segment Připraven kompilátorem součást spustitelného souboru Kód uţivatelských i knihovních funkcí Obvykle chráněn proti zápisu Datový segment Heap Zásobník int fce( void) {... { int (*fp)( void); fp = fce;... 79
Organizace paměti procesu datový segment IP R0 R1... SP Kódový segment Datový segment Heap Připraven kompilátorem součást spustitelného souboru Explicitně nebo implicitně (nulami) inicializované globální proměnné Řetězcové konstanty Data knihoven Zásobník int bigpole[ 1000]; { int* p = bigpole; char* s = "ahoj";... 80
Organizace paměti procesu - heap IP R0 R1... SP Kódový segment Datový segment Heap Vytvářen startovacím modulem knihoven Neinicializovaná dynamicky alokovaná data malloc/free ( C++: new/delete ) Obsazené bloky různé velikosti Zásobník + seznam volných bloků { char* s; s = malloc( 256);... 81
Organizace paměti procesu - zásobník IP R0 R1... SP Kódový segment Datový segment Heap Zásobník Připraven operačním systémem Lokální proměnné Pomocné proměnné generované kompilátorem Návratové adresy Aktivační záznamy funkcí { char pole[100]; char s[] = "Ahoj"; int x = 1 + 2 * 3;... 82
Statické proměnné globální proměnná neviditelná z jiných modulů C++: lepší řešení - namespace static int x; int fce( int a) { static int y = 0; return y += a; de facto globální (!) proměnná neviditelná z jiných funkcí inicializace: C: před vstupem do main C++: před prvním průchodem C++: raději skrýt do třídy 83
Organizace paměti procesu příklad const int max = 100; char buf[max]; co je v kterém segmentu? char* prefix( char* s) { static int n = 0; char* p = malloc( strlen( s) + 2); *p = '#'; strcpy( p + 1, s); return p; int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc 1]); p = prefix( buf); p = prefix( p);... 84
Struktury struct osoba { char jmeno[20]; char prijemni[30]; int rok_narozeni; int pohlavi; ; osoba os, *po, zam[20]; osoba beda = { "Béda", "Trávníček", 1980, p_muz ; definice struktury poloţky struktury struktura, ukazatel na strukturu, pole struktur strcpy( os.jmeno, "Venca"); zam[3].rok_narozeni = 1968; po = &zam[3]; po->pohlavi = p_muz; definice proměnné typu struktura s inicializací přístup k poloţkám (*x).y x->y 85
Typové kostrukce - přehled A x[ n] A x[] A * x pole n prvků typu A, n je konstantní výraz pole neznámého počtu prvků typu A (pouze v některých kontextech) ukazatel na typ A void * x ukazatel na neurčený typ *x ++x nelze A const * x const A * x ukazatel na konstantní hodnotu typu A ++x lze ++*x nelze A * const x konstantní ukazatel na typ A ++x nelze ++*x lze A & x A const & x const A & x A x() A x( par) A x( void) C++: reference na typ A C++: reference na konstantní hodnotu typu A funkce vracející typ A - C: bez určení parametrů, C++: bez parametrů funkce s určenými parametry funkce bez parametrů void x( par) funkce bez návratové hodnoty (procedura) 86
Kombinace typových kostrukcí A * x[10] pole ukazatelů A (* x)[10] ukazatel na pole A * x() funkce vracející ukazatel A (* x)() ukazatel na funkci A x[10]() pole funkcí - zakázáno A (* x[10])() pole ukazatelů na funkci A x()[10] funkce vracející pole - zakázáno A (* x())[10] funkce vracející ukazatel na pole typicky se nepouţívá pole = ukazatel na 1. prvek čtení deklarací: od identifikátoru doprava, aţ to nepůjde, tak doleva 87
Kombinace typových kostrukcí - příklad int*(*pf[10])(void); int*(*maso(int*(*p1)(void),int*(*p2)(void)))(void); co to je za maso??? 88
Kombinace typových kostrukcí - typedef int*(*pf[10])(void); int*(*maso(int*(*p1)(void),int*(*p2)(void)))(void); typedef int* fce( void); fce * pf [10]; fce * maso( fce* p1, fce* p2); pouţitím typedef se můţe výrazně zpřehlednit kód 89
Souborový vstup a výstup FILE * deskriptor souboru - deklaruje programátor FILE Neznámý obsah Pro knihovní funkce struktura definovaná v <stdio.h> Otevření souboru: kontrola existence a práv vytvoření vnitřních knihovních struktur asociace s otevřeným souborem předání deskriptoru souboru (FILE*) OS soubor (na disku) 90
Práce se soubory typ 'soubor' (ukazatel na strukturu) #include <stdio.h> FILE* fp; int c; if(!(fp = fopen("c:\\f.txt", "r"))) error(); while( (c = getc( fp))!= EOF) putchar( c); fclose( fp); otevření souboru pozor na '\\'!!! zavření souboru čtení ze souboru 91
Otevření souboru FILE* fopen( const char* fname, const char* mode); r open file for reading w truncate to zero length or create file for writing a append; open or create file for writing at end-of-file r+ open file for update (reading and writing) w+ truncate to zero length or create file for update a+ append; open or create f. for upd., writing at end-of-file rb binary file... mode soubor ex. soubor neex. seek r R Error 0 w Del, W W 0 w: soubor se smaţe +: vţdy čtení i zápis a W W End r+ R/W Error 0 w+ Del, R/W R/W 0 a+ R/W R/W End r: soubor musí existovat a: otevřít na konci 92
Textové vs. binární soubory Textový soubor konverze konců řádek ('\n') na platformově závislou vnější reprezentaci typicky 0x0D 0x0A (Win) nebo 0x0A (Unix) konverze je automatická, programátor se o to nemusí starat vhodné pro ukládání lidsky čitelných dat getc/putc, fgets/fputs, fprintf,... chování fseek/ftell na '\n' nedefinován - nepouţívat Binární soubor ţádné konverze se neprovádí v souboru je přesný binární obraz zapisovaných dat vhodné pro ukládání vnitřních datových struktur lidsky přímo nečitelné typicky fread/fwrite, lze i getc/putc (přístup po bajtech) fseek/ftell OK 93
Funkce pro práci se soubory FILE* fopen( const char* fname, const char* mode); int fclose( FILE* fp); int fprintf( FILE* fp, const char* format,...); int getc( FILE* fp); int putc( int c, FILE* fp); char* fgets( char* buffer, int limit, FILE* fp); int fputs( const char* buffer, FILE* fp); Zjištění velikosti souboru: fseek( fp, 0, SEEK_END); size = ftell( fp); int fread( void* ptr, int size, int n, FILE* fp); int fwrite( const void* ptr, int size, int n, FILE* fp); long ftell( FILE* fp); int fseek( FILE* fp, long offset, int whence); whence: SEEK_SET, SEEK_CUR, SEEK_END 94
Souborový vs. standardní v/v funkce pro práci se standardním vstupem/výstupem int getchar( void); int putchar( int c); int printf( const char* format,...); char* gets( char* buffer); standardní vstup/výstup FILE* stand. otevřený na čtení/zápis před vstupem do main FILE *stdin; FILE *stdout; getchar() getc(stdin) putchar(c) putc(c, stdout) Nepouţívat! Nelze ohlídat přetečení bufferu všechny souborové funkce lze pouţít i pro std. v/v FILE* fp = stdout; if(...) fp = fopen( "...", "r"); c = getc( fp); jednotný zápis na std výstup nebo do souboru 95
Základní knihovní (neobjektové) funkce <string.h> <cstring> strlen, strcmp, strcpy, strncpy, strcat, strchr, strstr, memset, memcmp, memcpy, memchr <stdio.h> <cstdio> getchar, putchar, fopen, fclose, getc, putc, fgets, fputs, fread, fwrite, ftell, fseek, printf, fprintf, vfprintf, fflush, ungetc FILE, stdin, stdout, EOF, SEEK_SET,... <stdlib.h> <cstdlib> malloc, free, atoi, atof, strtol, qsort, rand, exit <ctype.h> <cctype> isalpha, isdigit, isxdigit, isalnum, isspace, ispunct, iscntrl, islower, isupper, tolower, toupper <math.h> <cmath> abs, floor, sin, sqrt, exp, exp, log,... <time.h> <ctime>... a mnoho mnoho dalších time, gmtime, strftime, asctime, clock,... 96
Typy znaků <ctype.h> <cctype> Funkce vrací 0 nebo 1, podle toho zda zadaný znak je daného typu Parametrem funkce je jednotlivý ZNAK, ne celý řetězec isdigit číslice (0,..., 9) isxdigit hexadecimální číslice (0,..., 9, a,..., f, A,..., F) isalnum číslice nebo písmeno (0,..., 9, a,..., z, A,..., Z) isspace bílé znaky (mezera, tabulátor, konec řádku,...) ispunct interpunkční znaménka (?,!,.,...) iscntrl netisknutelné řídící znaky Problém v C: české znaky, isalpha písmeno (a,..., z, A,..., Z) ale i jiné diaktické znaky islower malé písmeno (a,..., z) isupper velké písmeno (A,..., Z) tolower k zadanému písmenu vrací příslušné malé písmeno toupper k zadanému písmenu vrací příslušné velké písmeno 97
Funkce qsort #include <stdio.h> #include <stdlib.h> #include <string.h> int compare( const void *arg1, const void *arg2 ) { return _stricmp( * ( char** ) arg1, * ( char** ) arg2 ); Ignore case void main( int argc, char **argv ) { int i; argv++; argc--; Uţivatelsky napsaná třídící funkce. Návratové hodnoty 0, < 0, a > 0. Parametry: pole k setřídění, počet prvků pole, velikost 1 prvku, porovánací funkce qsort( (void *)argv, (size_t)argc, sizeof( char * ), compare ); for( i = 0; i < argc; ++i ) printf( "%s ", argv[i] ); printf( "\n" ); 98
Direktivy preprocesoru #include <stdio.h> #include <cstdio> #include <iostream> #include "mymodul.h" #define KZR #define KZR 17 #define KZR( pzr) ((pzr) * 2) #undef #ifdef #ifndef #if #else #endif # ## knihovní headery C, C dle nových konvencí, C++ uţivatelské headery definice symbolu viz slajd Spojování modulů - #ifndef definice makra, lépe const int kzr = 17; definice parametrického makra raději vůbec nepouţívat, lépe inline funkce test na (ne)definovanost symbolu obrana před vícenásobným #include viz slajd Spojování modulů - #ifndef test konstantního výrazu - #if sizeof( int) == 4 'ouvozovkování' - #abc "abc" spojení identifikátorů - a##b ab 99
Spojování modulů problém x.c double A() { return B( 7); y.c double B() { return 3.14; error: Undefined 'B' 100
Spojování modulů externí deklarace x.c double B(); double A() { return B(); externí deklarace y.c double B() { return 3.14; 101
Spojování modulů nekonzistence x.c double B(); double A() { return B(); nekonzistence funkce B (počet a typy parametrů, návratová hodnota) y.c int B( int q) { return q+1; x.obj import B export A app.exe y.obj export B C: nedefinované chování C++: linker error 102
Spojování modulů header x.c #include "y.h" double A() { return B( 7); y.h int B( int q); y.c int B( int q) { return q+1; preprocesor int B( int q); double A() { return B( 7); hlavičkový soubor (header) 103
Spojování modulů nekonzistence x.c #include "y.h" double A() { return B( 7); y.h int B( int q); y.c double B() { return 3.14; int B( int q); double A() { return B( 7); nekonzistence 104
Spojování modulů řešení x.c #include "y.h" double A() { return B( 7); y.h int B( int q); y.c #include "y.h" double B() { return 3.14; int B( int q); double A() { return B( 7); int B( int q); double B() { return 3.14; error: Redefinition of 'B' 105
Spojování modulů duplicitní data x.c #include "y.h" double A() { int c; y.h y.c #include "y.h" int c; int c; double A() { int c; int c; x.obj export c export A y.obj export c linker error: Duplicate symbol 'c' 106
Deklarace vs. definice x.c #include "y.h" double A() { y.h extern int c; y.c #include "y.h" int c; extern int c; double A() { extern int c; int c; Definice x.obj import c export A Deklarace y.obj export c 107
Spojování modulů - typy Příklad definice nového typu teď není nutné chápat přesný význam Deklarace proměnné tohoto typu x.c #include "y.h" double A() { return C; y.h enum T { P, Q; extern enum T C; y.c #include "y.h" enum T C; enum T { P, Q; extern enum T C; double A() { return C; enum T { P, Q; extern enum T C; enum T C; 108
Spojování modulů - duplicita typů Přes y.h a z.h je t.h vloţen dvakrát error: Type redefinition: 'T' x.c #include "y.h" #include "z.h" double A() { return C+D; enum T { P, Q; extern enum T C; enum T { P, Q; extern enum T D; double A() { return C + D; t.h enum T { P, Q; y.h #include "t.h" extern enum T C; z.h #include "t.h" extern enum T D; 109
Spojování modulů - #ifndef x.c #include "y.h" #include "z.h" double A() { return C+D; t.h #ifndef _T_H #define _T_H enum T { P, Q; #endif není-li symbol definován... definice nového symbolu (makra) symbol jiţ definován nepřekládá se #ifndef _T_H #define _T_H enum T { P, Q; #endif extern enum T C; #ifndef _T_H #define _T_H enum T { P, Q; #endif extern enum T D; y.h #include "t.h" extern enum T C; z.h #include "t.h" extern enum T D; 110
Programování není zápis algoritmů Běhové prostředí programu Vazba programu na operační systém Přenositelnost mezi platformami Typické chyby a ochrana proti nim Ladění programů debuggerem a bez debuggeru Udrţovatelné programy, kultura programování 111
Vazba programu na operační systém Proces je izolován od ostatních procesů a jádra OS Virtuální adresový prostor a/nebo ochrana paměti Přímá komunikace s I/O zařízeními není moţná Přímá komunikace s jinými procesy by byla moţná technikou sdílené paměti Není ovšem všude dostupná a standardizována Pouţití efektivní ale obtíţné a nebezpečné Veškerá komunikace přes systémová volání Systémové volání zajišťuje: Přechod z uţivatelského reţimu do privilegovaného a zpět Moţnost suspendování procesu uvnitř systémového volání Konkrétní technika systémového volání závisí na HW a OS Softwarové přerušení, brány, speciální volání, falešné výjimky Obvykle není moţné volání přímo z C/C++ Relativně pomalé (změna kontextu, přeplánování) Mnoţina systémových volání je definována OS Můţe být přímo zpřístupněna knihovnou (Unix, "io.h") Nemusí být zveřejněna (Microsoft) 112
Zveřejněné rozhraní operačního systému "C-API" Zpřístupněno knihovnami pro C (výjimečně C++) Nejtypičtějsí část je standardizována Většina je závislá na OS Knihovní funkce obvykle provádějí více neţ jedno systémové volání Některé knihovny mohou zcela změnit původní logiku systémových volání Soubory: Buffering, překlad znakových sad, statefull/stateless Spouštění procesů: spawn = fork + exec Vnitřek knihovních funkcí můţe záviset na verzi OS Připojovány jako DLL v okamţiku startu procesu (Microsoft) Pozor na různé verze!! 113
Standardizovaná rozhraní OS stdio.h - souborový vstup a výstup Přístup "s ukazovátkem" Sjednocení přístupu k souborům a rourám stdin, stdout, stderr Buffering - sníţení počtu systémových volání Následky při pádu programu - fflush Textový/binární mód Překlad do jednotné formy - oddělovač řádků "\n" Neřeší adresářové sluţby signal.h, stdlib.h - řízení procesu Vyvolání/příjem signálu (podle Unixového vzoru) Ukončení procesu system( "winword my.doc") 114
Vlákna (threads) Pro realizaci serverů i některých GUI aplikací Je-li třeba souběţně vykonávat více činností Nebo čekat na více událostí různých druhů Proces můţe mít více vláken (threads) Všechna vlákna ţijí uvnitř společného adresového prostoru Kaţdé vlákno má vlastní zásobník (a tedy jiný SP) První vlákno je spuštěno při spuštění procesu Další vlákna vznikají na pokyn jiţ existujících vláken Vlákna běţí kvazi-paralelně, na multiprocesorech paralelně (!) Všechny moderní OS vlákna podporují Způsoby implementace se mohou výrazně lišit Lze je téţ implementovat na úrovni knihoven bez vědomí OS Norma C ani C++ o vláknech nehovoří Neexistuje přenositelný způsob práce s vlákny Existuje poměrně jednotná terminologie převzatá z teorie OS Existují pokusy o unifikaci prostřednictvím nestandardních knihoven Programování s vlákny je obtíţnější Nešikovná vzájemná komunikace vláken můţe zdrţovat i zablokovat Vyuţití vláken na multiprocesorech vyţaduje zvláštní opatrnost - Pozor na externí knihovny! 115
Odlišnosti mezi platformami Vlastnosti hardware Velikost adresového prostoru a velikostí ukazatelů Pořadí ukládání vícebajtových hodnot (little/big endian) Dostupné formáty celých a reálných čísel Vlastnosti operačního systému Znaková sadou (ASCII/EBCDIC, Win/ISO), oddělovače řádků Konvence jmen souborů (oddělovače, povolené znaky, délka) Další vlastnosti souborového systému (sémantika delete, links, přístupová práva) Základní sluţby a konvence OS (environment, registry) Konvence (/usr/bin,.exe, $HOME) Vlastnosti překladače Volba velikosti základních aritmetických typů Způsob zarovnání poloţek struktur Rozpoznávaná pod-/nad-mnoţinou jazyka Chyby v diagnostice a generovaném kódu Vlastnosti knihoven Dostupnost, pojmenování a sémantika funkcí 116
Přenositelnost mezi platformami Zákaz konstrukcí závislých na vlastnostech hardware a překladače int x; char * p = (char *)&x; struct { char a; int b; S; fwrite( &S, 1, sizeof( S), fp); Uţívání základních typů prostředníctvím typedef typedef unsigned long UINT32; Opatrné uţívání pokročilých konstrukcí (member-pointers, templates) Přednostní pouţívání funkcí definovaných normou jazyka Nelze-li jinak, uţívání direktiv #ifdef Ideál: Program přenositelný bez úpravy zdrojového textu #ifdef _MSC_VER // Microsoft typedef int64 INT64; const char delimiter = '\\'; #else typedef long long INT64; #ifdef UNIX const char delimiter = '/'; #else const char delimiter = '\\'; #endif #endif 117
Ladění programů debuggerem Spustit program v ladicím reţimu Některé zvládnou i připojení k jiţ běţícímu procesu (JIT Debugging) Ladicí reţim nemusí být (a typicky není) pro laděný program identický s normálním Většina funkcí debuggeru je moţná pouze pro programy přeloţené v ladicím nastavení překladače (bez optimalizací) Chybný program se můţe chovat při ladění jinak neţ finální verze Krokovat a spouštět program Odchytit chybující program a zobrazit stav těsně před chybou Nedestruktivně zastavit běţící program Nastavovat breakpointy do kódu Mohou být podmíněné Nastavovat breakpointy na data (změna či splnění podmínky) Některé typy mohou o několik řádů zpomalit běh programu Zobrazovat zásobník volání Zobrazovat lokální i globální proměnné Zobrazovat paměť laděného procesu 118
Ladění programů bez debuggeru Ladicí 'tisky' Co tisknout Kdy a kde tisknout Jak tisknout ('monitor', soubor, databáze,...) Automatické testování Testovací skripty Sady testovacích dat Lokalizace chyby Minimalizace zdrojového textu, kde se chyba vyskytuje Prevence proti zavlečeným chybám Ladicí implementace alokačních funkcí Obloţit kaţdý alokovaný blok prostorem vyplněným značkami Při dealokaci zkontrolovat neporušenost značek a změnit je 119
Bezpečné programování Zapnout všechna varování, která je schopen kompilátor vydat Upravit program do podoby, která nezpůsobí ţádné varování V odůvodněných případech lze varování vypnout pomocí #pragma Dodrţovat pravidla pro přenositelné a vícevláknové programy A to i kdyţ přenositelnost ani vícevláknovost zdánlivě nemá smysl Minimum globálních proměnných, pokud moţno pouze konstantní Procedura smí číst či měnit pouze objekty, které jsou přímo či nepřímo určeny jejími parametry Důsledné uţívání ochranných prostředků kompilátoru const, private,... Důsledná chybová diagnostika Test úspěšnosti kaţdého malloc, fopen,... Testy proti interním chybám a špatným parametrům Ochrana proti uţívání odalokovaných bloků Ochrana proti přetečením polí Všechny funkce manipulující s polem dostávají velikost pole jako parametr Ţádné strcat, gets a podobné nebezpečné funkce Největší nepřítel je chyba, která není vţdy a ihned smrtelná Dereference nulového ukazatele se pozná ihned Dereference neinicializovaného ukazatele způsobí pád později void f( int* p) { assert( p); /*...*/ free(p); p=0; 120
Udrţovatelné zdrojové texty Logické rozdělení do modulů a hlavičkových souborů na niţší úrovni datové struktury a funkce, třídy Jasné oddělení rozhraní od implementace Oddělení obsluhy uţivatelského rozhraní od vlastní logiky aplikace Minimum globálních proměnných ideálně ţádné, příp. třída (struktura) app Komentáře, zejména k rozhraním Indentace, omezená délka řádek Pojmenovávací konvence, logicky zvolené a dlouhé identifikátory Buďto my_big_array nebo MyBigArray Obvykle typy a konstanty začínají velkými písmeny, proměnné malými GetX/SetX konvence pro metody v C++ apod. Pojmenování všech smysluplných konstant Ţádné int x[ 100] ani case 27: Jaké jiné konstanty neţ smysluplné by měly být ve zdrojových textech? Nepouţívat deprecated features const a = 123; bool b; b++; char *s = "abcd"; <stdlib.h> 121
Dynamické seznamy struct osoba { char jmeno[20]; int narozen; osoba *dalsi; ; osoba *zamestnanci; ukazatel na další hlava seznamu jmeno Novak Jason Drson narozen 1905 dalsi 1948 1990 konec seznamu 122