Stáhnout prezentaci
Prezentace se nahrává, počkejte prosím
ZveřejnilTomáš Sedláček
1
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/
2
2 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
3
3 Programování v jazyce C OOP (v C++) ZS LS Obsah předmětu C++ CC Nejdůležitější: vlastní praxe Na přednáškách se nikdo nikdy programovat nenaučil
4
4 Obsah přednášky Přednáška 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?
5
5 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é........
6
6 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ýuka (v) C++ 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,...) 'Vše' již bylo naprogramováno
7
7 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)
8
8 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++
9
9 Historie 1970-73 první verze C, společný vývoj s UNIXem 1973přepsání jádra UNIXu do C 1978Kerninghan, Ritchie: The C Programming Language 1980standardy – ANSI X3J11, od r. 1999 ISO 9899 1980AT&T - "C with Classes" 1983poprvé název C++ (Rick Mascitti) 1985Stroustrup: The C++ Programming Language 1989ANSI X3J16 norma C++ 2003nejnově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)
10
10 hello.c #include #include int main( int argc, char ** argv) { printf( "Hello\n"); getch(); return 0; }
11
11 hello.c #include #include int main( int argc, char ** argv) { printf( "Hello\n"); getch(); return 0; } tělo funkce hlavička funkce příkaz deklarace knihovních funkcí direktiva preprocesoru vložení souboru formální parametry název funkce typ návratové hodnoty skutečné parametry volání funkce bez parametrů složené závorky BEGIN END
12
12 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
13
13 Překlad jednoho modulu a sestavení.cpp.h CC.objLink.exe.obj.lib kompilace spojování (linkování) knihovny standardní i jiné knihovní headery objektový modul (přeložený kód) spustitelný program
14
14 Modul - ucelená funkčí jednotka modul.cpp (.c) - implementace modul.h - definice rozhraní Oddělený překlad - dělení na moduly fotbal.cpp fotbal.h hriste.cpphrac.cppmic.cpp hriste.hhrac.hmic.h rozdělení projektu do modulů a vytváření headerů je umění, nedá se to naučit na přednášce
15
15 Překlad více modulů – oddělený překlad.c.h CC.objLink.exe.obj.lib.obj kompilace jednoho modulu knihovny knihovní headery vlastní headery.c další moduly
16
16 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 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) raději nepoužívat
17
17 Integrované vývojové prostředí.c.h CCLink.exe.obj.lib.obj.c debugger projekt editor
18
18 Make.c.h CCLink.exe.obj.lib.obj.c make makefile
19
19 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
20
20 Funkce Základní programová jednotka je funkce Neexistují vnořené funkce Začátek programu – funkce main int fce1( int x, int y) { return x+y; } int fce2( int a) { return fce1( 1, 2*a+5); } int main( int argc, char** argv) {... } int fce2( int a) { int fce1( int x, int y) { return x+y; } return fce1( 2*a+17); } vnořené funkce nelze! začátek programu argumenty z příkazové řádky později hlavička funkce tělo funkce
21
21 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 int fce1( int x, int y) { return x+y; } void fce2( char* text) { printf( text); } void fce3( char* text) { if( ! text) return; printf( text); } návrat celočíselné hodnoty návrat z funkce typ funkce
22
22 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 int fce1( int x, int y, float z) {... } int fce2( double a, char* str) {... } int fce3( void) {... } int fce4( int x, y) {... } int fce5 {... } každý parametr musí mít typ
23
23 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"); }
24
24 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 !!!
25
25 Příklad - malá násobilka #include int vynasob( int c) { int i = 1, v; if( 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; } inicializovaná celočíselná proměnná neinicializovaná proměnná hlavička funkce, formální parametr definice knihovních funkcí konec cyklu začátek cyklu kontrola parametrů okanžitý návrat z funkce 2 * 7 = 14 i++ konec, OK hlavní program ignorování návratové hodnoty volání funkce se skutečným parametrem The END
26
26 Předávání parametrů hodnotou Všechny parametry se předávají hodnotou 'Výstupní' parametry pouze přes ukazatele nebo reference vymění se jen lokální proměnné funkce předají se pouze hodnoty (1, 2), nikoliv 'proměnné' 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; }
27
27 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 int g = 1; void fce( int x) { int z = 2; g = g + z * x; } int main( void) { fce( 2); fce( 3); return g; } globální proměnná formální parametr skutečný parametr lokální proměnná co vrátí main ??
28
28 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
29
29 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
30
30 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) 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) printf( "OK"); else printf( "nee"); syntakticky správně, ale dělá něco jiného syntakticky špatně if( a > 1) { b = 0; printf( "OK"); } else { printf( "nee"); }
31
31 Vnořené podmíněné příkazy if( a > 1) { if( b > 0) printf( "OK"); } else { printf( "nee"); } 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");
32
32 Vícenásobné větvení – konstrukce switch switch (výraz) { case konstanta: posloupnost příkazů break; case konstanta: posloupnost příkazů break; default: posloupnost příkazů } 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; } switch( ch) { case '0'..'9': x += ch - '0'; break; case 'A'..'F': x += ch - 'A'; break; } switch( errc) { case 0: b = 0; printf( "OK"); case -1: printf( "spatne"); break; } zapomenutý break syntakticky OK, ale dělá něco jiného interval nelze celočíselný výraz ukončení větve pokud výraz není roven žádné z konstant
33
33 Cyklus – konstrukce while a do-while while (výraz) příkaz podmínka na začátku do příkaz while (výraz) ; podmínka na konci while( a > 1) { fce( a); a = a / 2; } while( a > 1) a = a / 2; do { fce( a); a = a / 2; } while( a > 1); tělo se vždy alespoň jednou provede Pozor! cyklus pokračuje pokud je podmínka platná
34
34 for (výraz1 ; výraz2 ; výraz3 ) příkaz je ekvivalentem výraz1; while (výraz2 ) { příkaz výraz3 ; } Cyklus – konstrukce for i=0; while( i<=9) { fce( i); i=i+1; } inicializace for( i=0; i<=9; i=i+1) { fce( i); } podmínkainkrementtělo cyklu FOR I := 0 TO 9 DO FCE(I) ekvivalent v jiném jazyce for( init(a); i 0; a[i++]=i*2) { fce( i); } for v C++: obecnější, širší možnosti použití jako inicializaci, podmínku i inkrement lze libovolný výraz
35
35 breakokamžité ukončení celého cyklu cyklus s podmínkou uprostřed ošetření chybových stavů continueukončení (jednoho) běhu těla cyklu Ukončení cyklu - break, continue for(;;) { errc = fce(); if( errc < 0) break; jinafce(); } n = 1; while( n<1000) { val = get(); if( val == 0) continue; n = n * val; }
36
36 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ů Goto 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 label návěští
37
37 Celočíselné typy typ8 bit16 bit32 bit64 bit char8888 short8 / 1616 16 / 32 int16 32 long1632 32 / 64 long --64 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 -2GB.. +2GB
38
38 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) charnorma neurčuje signed / unsigned korektní porovnání na nerovnost pouze 0.. 127 'a' < 'ž' ? signed char-128.. 127 unsigned char0.. 255 - 'byte' wchar_tstddef.h: znak roz ší řen é sady (Unicode) časté použití: test (ne)nulovosti např. ukazatelů záleží na implementaci většinou char = signed 40 > 200 !!! 200 -56
39
39 enum pohlavi { p_muz, p_zena }; pohlavi p = p_muz; int pp = p_zena; pohlavi q = 0; enum flags { f1 = 1, f2 = 2, f3 = 4, f4 = 8 }; if( x & f1)... enum porty { pop3 = 111, ftp = 21, smtp = 80 }; Výčtový typ C: celočíselné konstanty - OK C++: samostatný typ - nelze test bitů hodnoty doplní překladač (od 0) explicitní hodnoty
40
40 'Reálné' typy float double long double malá přesnost - nepoužívat! standard pro 'reálné' výpočty zvýšená přesnost double x = 1; double y = x / 3; if( x == 3 * y) printf( "Presne"); else printf( "Nepresne"); Pozor! Reálné výpočty jsou vždy nepřesné 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
41
41 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 Automatické konverze u binárních operací signed int unsigned int signed long unsigned long float double long double vždy když je použit menší typ než int při nestejných operandech
42
42 Přehled operátorů, asociativita a priorita postfix ++ -- ( ) [ ] ->. :: post-in/de-krementace volání funkce index přístup k položce struktury kvalifikace identifikátoru prefix ++ -- ! ~ + - & * sizeof ( ) new delete 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 >= uspořádání L== !=rovnosti L&bitové AND L^bitové XOR L|bitové OR L&&booleovské AND L||booleovské OR L? :podmíněný výraz P= *= /= %= += -= &= ^= |= >= přiřazení kombinované přiřazení L,sekvence
43
43 Základní aritmetické operátory + - * / % podle typu operandů automatická konverze na větší typ % - modulo x / y1x / b1.666 x % y2x % bError a / b1.666a / y1.666 a % bErrora % yError int x=5, y=3; double a=5, b=3; modulo je pouze celočíselná operace celočíselné dělení reálné dělení
44
44 Bitové a logické operátory & | ^ ~ - bitové operace AND, OR, XOR, NOT && || !- logické operace AND, OR, NOT 5 & 315 && 31 5 | 375 || 31 5 ^ 365 ^^ 3Error 5 ^ 1510 5 & 005 && 00 5 | 055 || 01 neexistuje 5 = 0101 2 3 = 0011 2 1 = 0001 2 7 = 0111 2 9 = 1001 2 15 = 1111 2 10 = 1010 2 oba op. 0 alespoň jeden operand 0 alespoň jeden operand = 0
45
45 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) = > == != 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í ! 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 ! if( x = 1)... POZOR!!! Přiřazení! (zde hodnota vždy = 1)
46
46 Přiřazení, inkrementace, bitový posun = += -= *= /= %= &= |= ^= >= kombinované přiřazení a op= b a = a op b ++ -- 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) > bitový posun C++ - časté použití pro jiné účely (streams) - přetěžování i += 2; x[ i+=1] /= 3; int sum = 0; int i, x[10];... for( i=0; i<9; sum += x[i++]) ; pozor - vždy si uvědomit, zda jde o pre- nebo post- inkrementaci
47
47 Podmíněný výraz, sekvence a ? b : c po vyhodnocení podmínky a se vyhodnotí buď b (je-li a != 0) nebo c (je-li a == 0) a, b po úplném vyhodnocení a se vyhodnotí b x = (y>0 ? y : 0); x = (tmp = y, y = y + 1, tmp); ekvivalent x = y++; ternární operátor operátor sekvence ('zapomnění')
48
48 i = 0; p[ i++] = i++; 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 možné výsledky: p[0] = 0; p[1] = 0; p[0] = 1;
49
49 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++; Nadbytečné volání funkce s vedlejším efektem if ( getchar() == 'A' && getchar() == 'B' ) nevím, jestli se provede
50
50 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 Vícerozměrné pole je pole polí Deklarace: t x[m][n]; Přístup: x[m][n] = a; int x[5]; for( i=1; i <=5; i++) x[i] = i; 01234??? ?12345 Přepis náhodného místa v paměti ! Nepředvídatelné následky !! 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 význam: m-prvkové pole typu (n-prvkové pole typu t)
51
51 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 } };
52
52 Ukazatele Co to je proměnná? místo v paměti, typ (-> velikost), hodnota hodnota se dá číst a většinou i zapisovat 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á 1 někde bydlí je nějak velká (typ) má hodnotu
53
53 Ukazatele Deklarace: t x – proměnná x typu t t *p - ukazatel p na typ t Operátor reference:p = &x Operátor dereference:x = *p Neinicializovaný ukazatel vs. nulový ukazatel C:#define NULL 0 C++:0 17 x: p:
54
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; přepsání náhodného místa v paměti 1 x::px 3 y::py ? ? 5 jaký zde bude obsah x a y?
55
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; typicky 'segmentation fault' váš program bude ukončen pro pokus o porušení ochrany paměti 1 x::px 3 y::py 0: 7 umožní test
56
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; přístup k hodnotě proměnné přes ukazatel 1 x::px 4 y::py Pozor na prioritu! *py++ *(py++) 1 x::px 3 y::py
57
57 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; 1 x::px 1 y::py 9 x::px 1 y::py jaký zde bude obsah x a y?
58
58 Pole a ukazatele, aritmetika ukazatelů int a[5]; int *p; a[2] = 20; p = &a[2]; a[0] = *p - 15; p++; *p = 30; 01234 ??20?? 5? 3030? p p reference na prvek pole inkrementace ukazatele posun na daší prvek a
59
59 Pole a ukazatele, aritmetika ukazatelů p = &a[1]; *(p+3) = 40; Operátor [] p[i] *(p+i) &p[i] p+i a &a[0] 5?2030304040 p přičtení čísla k ukazateli posun o n prvků identifikátor pole je ukazatel na svůj nultý prvek indexování pole (ukazatele) je jen jiný zápis přístupu přes ukazatel 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]
60
60 Pole a ukazatele, aritmetika ukazatelů int a[5]; int *p; identifikátor pole je konstantní nelze do něj přiřazovat 010203040 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]; a[4] = p + 1; p = a[0]; p[1] = a; a = p + 2; nekompatibilní typy nestejná úroveň indirekce p[i] *(p+i) *(i+p) i[p]
61
61 Řetězce Jazyk C++ nezná pojem řetězec (!) – konvence, knihovny Řetězec je pole znaků (char) zakončené nulou "Ahoj" Xproměnná 'X'znaková konstanta - celočíselná hodnota "X"řetězec - ukazatel 'A''h''o''j''\0' Každý řetězec musí být vždy ukončen nulou 'A''h''o''j''\0' vždy myslet na koncovou nulu ! pozor na uvozovky a apostrofy ! char buffer[4]; strcpy( buffer, "Ahoj"); '\0' = 0 kód znaku v použitém kódování (ASCII, CP1250, ISO08859-2, EBCDIC,...) když chcete říct mezera, napište mezeru (' ') ať vás ani nenapadne napsat 32 přestože to na VAŠEM počítačí funguje
62
62 'Řetězcové proměnné' char s1[] = "Uno"; const char *s2 = "Due"; 'U''n''o''\0' 'D''u''e''\0' s1: s2: Inicializovaná proměnná typu ukazatel s2++ se přesune na další znak Inicializované pole (konstantní ukazatel) s1++ nelze! anonymní globální proměnná const char[]
63
63 Ř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 int strlen( const char* s); Ahoj\0??? pole: počet znaků (bez koncové nuly) char pole[8] = "Ahoj"; x = strlen( pole);// 4 skutečný počet znaků (4) nikoliv velikost pole deklarace řetězcových funkcí inicializované pole typu char
64
64 Délka řetězce – různé způsoby implementace int i = 0; while ( *s != '\0') { i++; s++; } return i; int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } char *p = s; while (*p++) ; return p-s-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++; přístup přes index přístup přes ukazatel podmínka for cyklu může být libovolná 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 !
65
65 Řetězce - kopírování char* strcpy( char* d, const char* s); zkopíruje obsah s do místa začínajího od d char buf[8]; char pozdrav[] = "Ahoj"; strcpy( buf, pozdrav); Ahoj\0 pozdrav Ahoj\0??? buf kopíruje pouze do koncové '\0'
66
66 Řetězce – chyby při kopírování Dobryden\0 pozdrav buf char buf[6]; char pozdrav[] = "Dobry den"; strcpy( buf, pozdrav); Dobryden\0 vždy pozor na dostatek místa funkce nic nekontroluje !!! váš program provedl... char *ptr; char pozdrav[] = "Ahoj"; strcpy( ptr, pozdrav); kopírování na neinicializovaný ukazatel !!! váš program provedl... Ahoj\0 pozdrav ptr ? Ahoj\0 ptr neinicializovaný !!!
67
67 Řetězce – zřetězení, vyhledávání char* strcat( char* d, const char* s);připojí s za d char* strchr( const char* s1, int c);vyhledá první pozici c v s1 char* strstr( const char* s1, const char* s2);vyhledá podřetězec s2 v s1 char buf[10]; char* bp; strcpy( buf, "Ahoj"); strcat( buf, "Babi"); bp = strstr( buf, "oj"); Ahoj\0????? po strcpy AhojBabi\0? po strcat buf bp AhojBabi\0? pozor na dostatek místa !
68
68 Řetězce – porovnávání a další funkce int strcmp( const char* s1, const char* s2); s1 < s2 -1 s1 = s2 0 s1 > s2 +1 lexikografické uspořádání (jako ve slovníku) 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 co znamená 's1 < s2'? ABCE === ABCDE výsledek podle prvního rozdílného znaku
69
69 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ů #include 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); %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) printf ("%s", r); printf ("%i + %i = %i", c, c);
70
70 Parametry příkazové řádky C:\> myprog.exe -n -w a.txt b.txt 0 myprog.exe\0 -n -w a.txt b.txt argv 5 argc int main( int argc, char** argv) pole řetězců (ukazatelů na char) Počet parametrů včetně názvu programu ! = počet ukazatelů v argv
71
71 Zpracování příkazové řádky – výpis parametrů int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; } C:\> myprog.exe -n -w a.txt b.txt myprog.exe -n -w a.txt b.txt výstup řetězce a odřádkování posun na další parametr
72
72 Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; } myprog.exe\0 -n -w a.txt b.txt argv 0 typ: char** **argv argv[0][0] typ: char argv[4][1] *argv argv[0] typ: char* argv[4]
73
73 Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; } myprog.exe\0 -n -w a.txt b.txt argv 0 argv++ **argv
74
74 Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; } myprog.exe\0 -n -w a.txt b.txt argv 0 *argv == 0
75
75 Zpracování příkazové řádky 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 usage: myprog [-n] [-w] fileA fileB nastavení přepínače zbývající parametry výkonná funkce prg.exe\0 -n -w a.txt b.txt 0 argv
76
76 Dynamická alokace paměti C: standardní knihovny void* malloc( int size); void free( void* p); C++: součást jazyka (podrobně později) new Tnew T[ n] delete pdelete[] p char* s; s = malloc( 20); if( ! s) error(); strcpy( s, "ahoj"); *s = 'X'; s: Xhoj\0 C++ nutnost přetypování s = (char*) malloc( 20); lépe new vždy ověřit !!! váš program provedl... int * pole; pole = malloc(20 * sizeof(int)); Velikost datového typu sizeof(char) = 1 vždy
77
77 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'
78
78 Organizace paměti procesu Kódový segment Kód programu Datový segment Globální proměnné Heap Dynamicky alokovaná data Zásobník Lokální proměnné a parametry funkcí IP R0 R1... SP
79
79 Organizace paměti procesu – kódový segment 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 IP R0 R1... SP int fce( void) {... } { int (*fp)( void); fp = fce;...
80
80 Organizace paměti procesu – datový segment Kódový segment Datový segment Připraven kompilátorem součást spustitelného souboru Explicitně nebo implicitně (nulami) inicializované globální proměnné Řetězcové konstanty Data knihoven Heap Zásobník IP R0 R1... SP int bigpole[ 1000]; { int* p = bigpole; char* s = "ahoj";...
81
81 Organizace paměti procesu - heap 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 + seznam volných bloků Zásobník IP R0 R1... SP { char* s; s = malloc( 256);...
82
82 Organizace paměti procesu - zásobník 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í IP R0 R1... SP { char pole[100]; char s[] = "Ahoj"; int x = 1 + 2 * 3;...
83
83 Statické proměnné static int x; int fce( int a) { static int y = 0; return y += a; } globální proměnná neviditelná z jiných modulů 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++: lepší řešení - namespace C++: raději skrýt do třídy
84
84 Organizace paměti procesu – příklad const int max = 100; char buf[max]; 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);.... } co je v kterém segmentu?
85
85 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 }; strcpy( os.jmeno, "Venca"); zam[3].rok_narozeni = 1968; po = &zam[3]; po->pohlavi = p_muz; definice struktury položky struktury struktura, ukazatel na strukturu, pole struktur přístup k položkám (*x).y x->y definice proměnné typu struktura s inicializací
86
86 Typové kostrukce - přehled A x[ n] pole n prvků typu A, n je konstantní výraz A x[] pole neznámého počtu prvků typu A (pouze v některých kontextech) A * x 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 C++: reference na typ A A const & x const A & x C++: reference na konstantní hodnotu typu A A x() funkce vracející typ A - C: bez určení parametrů, C++: bez parametrů A x( par) funkce s určenými parametry A x( void) funkce bez parametrů void x( par) funkce bez návratové hodnoty (procedura)
87
87 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 čtení deklarací: od identifikátoru doprava, až to nepůjde, tak doleva typicky se nepoužívá pole = ukazatel na 1. prvek
88
88 int*(*pf[10])(void); int*(*maso(int*(*p1)(void),int*(*p2)(void)))(void); Kombinace typových kostrukcí - příklad co to je za maso ???
89
89 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); Kombinace typových kostrukcí - typedef použitím typedef se může výrazně zpřehlednit kód
90
90 Souborový vstup a výstup struktura definovaná v Neznámý obsah Pro knihovní funkce FILE * FILE deskriptor souboru - deklaruje programátor 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*) soubor (na disku) OS
91
91 #include FILE* fp; int c; if( !(fp = fopen("c:\\f.txt", "r"))) error(); while( (c = getc( fp)) != EOF) putchar( c); fclose( fp); Práce se soubory typ 'soubor' (ukazatel na strukturu) otevření souboru čtení ze souboru zavření souboru pozor na '\\' !!!
92
92 Otevření souboru FILE* fopen( const char* fname, const char* mode); ropen file for reading wtruncate to zero length or create file for writing aappend; 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 rbbinary file... modesoubor ex.soubor neex.seek rRError0 wDel, WW0 aWWEnd r+R/WR/WError0 w+Del, R/WR/WR/W0 a+R/WR/WR/WR/WEnd +: vždy čtení i zápis a: otevřít na konci r: soubor musí existovat w: soubor se smaže
93
93 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
94
94 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); 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 Zjištění velikosti souboru: fseek( fp, 0, SEEK_END); size = ftell( fp);
95
95 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) FILE* fp = stdout; if(...) fp = fopen( "...", "r"); c = getc( fp); jednotný zápis na std výstup nebo do souboru Nepoužívat! Nelze ohlídat přetečení bufferu všechny souborové funkce lze použít i pro std. v/v
96
96 Základní knihovní (neobjektové) funkce strlen, strcmp, strcpy, strncpy, strcat, strchr, strstr, memset, memcmp, memcpy, memchr getchar, putchar, fopen, fclose, getc, putc, fgets, fputs, fread, fwrite, ftell, fseek, printf, fprintf, vfprintf, fflush, ungetc FILE, stdin, stdout, EOF, SEEK_SET,... malloc, free, atoi, atof, strtol, qsort, rand, exit isalpha, isdigit, isxdigit, isalnum, isspace, ispunct, iscntrl, islower, isupper, tolower, toupper abs, floor, sin, sqrt, exp, exp, log,... time, gmtime, strftime, asctime, clock,...... a mnoho mnoho dalších
97
97 Typy znaků 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 isalpha – písmeno (a,..., z, A,..., Z) 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 Problém v C: české znaky, ale i jiné diaktické znaky
98
98 Funkce qsort #include int compare( const void *arg1, const void *arg2 ) { return _stricmp( * ( char** ) arg1, * ( char** ) arg2 ); } void main( int argc, char **argv ) { int i; argv++; argc--; qsort( (void *)argv, (size_t)argc, sizeof( char * ), compare ); for( i = 0; i < argc; ++i ) printf( "%s ", argv[i] ); printf( "\n" ); } Uživatelsky napsaná třídící funkce. Návratové hodnoty 0, 0. Parametry: pole k setřídění, počet prvků pole, velikost 1 prvku, porovánací funkce Ignore case
99
99 Direktivy preprocesoru #include #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
100
100 x.c double A() { return B( 7); } Spojování modulů – problém error: Undefined 'B' y.c double B() { return 3.14; }
101
101 x.c double B(); double A() { return B(); } Spojování modulů – externí deklarace externí deklarace y.c double B() { return 3.14; }
102
102 Spojování modulů – nekonzistence C: nedefinované chování C++: linker error x.c double B(); double A() { return B(); } y.c int B( int q) { return q+1; } x.obj import B export A y.obj export B app.exe nekonzistence funkce B (počet a typy parametrů, návratová hodnota)
103
103 Spojování modulů – header hlavičkový soubor (header) x.c #include "y.h" double A() { return B( 7); } y.c int B( int q) { return q+1; } y.h int B( int q); int B( int q); double A() { return B( 7); } preprocesor
104
104 Spojování modulů – nekonzistence nekonzistence x.c #include "y.h" double A() { return B( 7); } y.c double B() { return 3.14; } y.h int B( int q); int B( int q); double A() { return B( 7); }
105
105 Spojování modulů – řešení x.c #include "y.h" double A() { return B( 7); } y.c #include "y.h" double B() { return 3.14; } y.h int B( int q); int B( int q); double A() { return B( 7); } int B( int q); double B() { return 3.14; } error: Redefinition of 'B'
106
106 y.obj export c Spojování modulů – duplicitní data x.c #include "y.h" double A() {} y.c #include "y.h" int c; y.h int c; int c; double A() {} int c; x.obj export c export A linker error: Duplicate symbol 'c'
107
107 y.obj export c Deklarace vs. definice x.c #include "y.h" double A() {} y.c #include "y.h" int c; y.h extern int c; extern int c; double A() {} extern int c; int c; x.obj import c export A Deklarace Definice Deklarace Definice
108
108 Spojování modulů - typy x.c #include "y.h" double A() { return C; } y.c #include "y.h" enum T C; y.h enum T { P, Q}; extern 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; Příklad definice nového typu teď není nutné chápat přesný význam Deklarace proměnné tohoto typu
109
109 Spojování modulů - duplicita typů x.c #include "y.h" #include "z.h" double A() { return C+D; } t.h enum T { P, Q}; enum T { P, Q}; extern enum T C; enum T { P, Q}; extern enum T D; double A() { return C + D; } y.h #include "t.h" extern enum T C; z.h #include "t.h" extern enum T D; error: Type redefinition: 'T' Přes y.h a z.h je t.h vložen dvakrát
110
110 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 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; symbol již definován nepřekládá se není-li symbol definován... definice nového symbolu (makra)
111
111 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í
112
112 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)
113
113 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 !!
114
114 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")
115
115 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!
116
116 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í
117
117 Přenositelnost mezi platformami Zákaz konstrukcí závislých na vlastnostech hardware a překladače 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 #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 int x; char * p = (char *)&x; struct { char a; int b; } S; fwrite( &S, 1, sizeof( S), fp); Ideál: Program přenositelný bez úpravy zdrojového textu
118
118 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
119
119 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
120
120 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;
121
121 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";
122
122 Dynamické seznamy struct osoba { char jmeno[20]; int narozen; osoba *dalsi; }; osoba *zamestnanci; hlava seznamu jmenoNovak narozen1905 dalsi Jason 1948 Drson 1990 ukazatel na další konec seznamu hlava seznamu ukazatel na další
123
123 Vyhledání prvku osoba *os; for( os=zamestnanci; os; os=os->dalsi) { if( os->narozen == 1972) return os; } return 0; zamestnancios Karel 1972 Novak 1905 Jason 1948 Drson 1990 přechod na další prvek pozor na konec! lezu po krabicích
124
124 Přídání prvku na začátek osoba *zamestnanci;... osoba *novy; if( ! (novy = malloc(sizeof(osoba)))) error(); strcpy( novy->jmeno, "Novak"); novy->narozen = 1905; novy->dalsi = zamestnanci; zamestnanci = novy; nový prvek Novak 1905 Jason 1948 Drson 1990 zamestnanci zařazení do seznamu novy
125
125 Přídání prvku doprostřed osoba *sem, *novy; if( ! (novy = malloc(sizeof(osoba)))) error(); strcpy( novy->jmeno, "Karel"); novy->narozen = 1972; novy->dalsi = sem->dalsi; sem->dalsi = novy; Novak 1905 Jason 1948 Drson 1990 zamestnancisem Karel 1972 novy přepojení ukazatelů
126
126 Dynamické datové struktury - fronta struct prvek { int n; prvek *dalsi; }; struct fronta { prvek* prvni; prvek* posledni; }; init( fronta* f); put( fronta* f, int n); int get( fronta* f); fronta f; init( &f); put( &f, 1); x = get( &f); 1 ukazatel na další 23 prvni: posledni: fronta prvek poslední prvek nulový ukazatel
127
127 Další dynamické struktury obousměrně propojený lineární seznam binární strom, vyvážený strom obecný strom, obecný graf gumové pole asociativní pole mnoho DS ve standardních knihovnách - C++ STL
128
128 Oblíbené chyby – struktura programu Chybějící nebo přebývající středník: for (i=0; i<n; i++); {... } funkce(); {... } Přehozené parametry funkcí nebo části konstrukce: char text[20]; strcpy( "Chyba!", text); for( i=0; i++; i<20)... Nezapomínat na break v konstrukci switch Pozor na define (raději takhle vůbec nepoužívat): #define uint int* uint p1,p2; #define N 100; int delka=N+1; /* rozvine se: int delka=100;+1; */ #define square (x) x*x K čemu patří else? if(podmínka1) if(podmínka2) /* akce */ else /* jiná akce */
129
129 Oblíbené chyby – výrazy Celočíselné dělení: x=1/3; Zaokrouhlovací chyby: for (x=0; x!=1.0; x+=0.1)... Odporující si parametry u printf: printf("%d",1.25); Záměna & | a && || a=1; b=2; if(a&b)... Zkrácené vyhodnocování logických výrazů Pozor na záměnu znaků: a=1; if (a<1,5)... Pozor na prioritu operátorů. Závorka navíc nikdy neuškodí. Nespoléhat na pořadí vyhodnocování (následující výstupy nejsou definovány): printf("%d %d",i++,i--); a[i++]=b[i++] může být přeloženo 3 způsoby if (0<x<1)...
130
130 Oblíbené chyby – ukazatele Neinicializované proměnné, zvl. ukazatele: char *text; strcpy( text, "Chyba!"); Ukazatel na proměnnou, která už neexistuje: char text[20]; strcpy( text,....); return text; Chybějící nebo naopak přebývající & u parametru předávaného ukazatelem: scanf( "%d %s", i, &text);
131
131 Co (a proč ) dělá tento program? #define _ F-->00||F-OO--; int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ }
132
132 ==================
133
133 Nepoužité slajdy
134
134 Preprocesor /*...*/ int printf( const char *,...); /*...*/ int getch(); int putch(); /*...*/ #include #include int main( int argc, char ** argv) { printf( “Hello\n”); getch(); return 0; /*...*/ int printf( const char *,...); /*...*/ int getch(); int putch(); /*...*/ int main( int argc, char ** argv) { printf( “Hello\n”); getch(); return 0; } stdio.h conio.h hello.c
135
135 Typy souborů MicrosoftUnix.c.c.c.cZdrojový kód C.cpp.C.cc.cppZdrojový kód C++.h Hlavičkový soubor C.h.hpp nebo bez přípony.h.H.hh.hpp nebo bez přípony Hlavičkový soubor C++.i (Výstup preprocesoru).asm.s(Přeložený kód v assembleru).obj.oObjektový modul.lib.aKnihovna.dll.soDynamicky linkovaná knihovna.exebez příponySpustitelný program
136
136 C a C++ C++ nadstavba C (až na drobné výjimky) lepší C (reference, implicitní parametry, přetěžování funkcí) podpora pro datovou abstrakci objektově orientované programování zajímavé a pokročilé vlastnosti – výjimky, RTTI, šablony výborné objektové knihovny - STL encapsulationzapouzdření inheritancedědičnost polymorphysmpolymorfismus
137
137 Výhody a nevýhody jazyka C přenositelnost standardní součást UNIXudědictví UNIXu úsporná syntaxenepřehledná syntaxe dostupnost a kvalita kompilátorůslabá kontrola při kompilaci využívání prostředků OSžádná kontrola za běhu spojitelnost s jinými jazyky použitelnost pro OS a I/O použitelnost pro pračky řezničiny
138
138 Zajímavé vlastnosti C Přenositelnost velikost int, direktivy Zahození hodnoty Příkazem může být výraz (např. přiřazovací výraz) Jazyk samotný nezná žádné 'standardní funkce' include - definice funkcí, typů, dat,... bohaté knihovny, různá prostředí - různé knihovny (pračky nepotřebují souborový výstup) Neexistence automatických kontrol - chybové kódy C++ - mechanismus výjimek časté 'padání' nebo neočekávané chování programu Váňovo paradoxon čím vyšší jazyk tím méně přenositelný
139
139 Přeložený kód - modul hello.obj... push call... call... ret _main _printf _getch 'H', 'e', 'l', 'l', 'o', 10, 0 Data Export Import Code
140
140 Spojování modulů hello.obj... call... _main _printf _getch Export Import Code cstart.obj... call... entry point _main Export Import Code printer.obj... _printf Export Import Code
141
141 Dynamická vs. lokální data char buf[32] = "abc"; char* fce( int c) { char* s; s = malloc( 20); strcpy( s, "ahoj"); *s = c; return s; } "abc" "ahoj" ??? "ahoj" Lokální zásobník Dynamická heap Globální buf: c:c: s: lokální ukazatel na dynamická data
142
142 Dynamická vs. lokální data char buf[32] = "abc"; char* fce( int c) { char* s; s = malloc( 20); strcpy( s, "ahoj"); *s = c; return s; } { char* str = fce( 'X'); "abc" "ahoj" 'X' "Xhoj" Dynamická heap Globální buf: c:c: s: str zatím není inicializované str: Lokální zásobník ???
143
143 Dynamická vs. lokální data char buf[32] = "abc"; char* fce( int c) { char* s; s = malloc( 20); strcpy( s, "ahoj"); *s = c; return s; } { char* str = fce( 'X'); "abc" "ahoj" "Xhoj" Dynamická heap Globální buf: návrat ukazatele na dynamická data str: Lokální zásobník
144
144 Ukazatele na struktury struct osoba { char jmeno[20]; int narozen; }; osoba ja, zamestnanci[1000]; osoba *novy; if(!(novy = malloc(sizeof(osoba)))) error(); novy->narozen = 1968; strcpy( novy->jmeno, "Novak"); Pozor! *x.y *(x.y) x->y (*x).y
145
145 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 void * my_malloc( size_t s) { char * p = (char *)malloc( s+sizeof(size_t)+2*AB_SIZE); if ( ! p ) return 0; *(size_t *)p = s; memcpy( p+sizeof(size_t), AB_FILL, AB_SIZE) memcpy( p+sizeof(size_t)+AB_SIZE+s, AB_FILL, AB_SIZE) return p+sizeof(size_t)+AB_SIZE; } void my_free( void * vp) { char * p; if ( ! vp ) ERROR(); p = (char *)vp - (sizeof(size_t)+AB_SIZE); if ( memcmp( p+sizeof(size_t), AB_FILL, AB_SIZE) ) ERROR(); if ( memcmp( p+sizeof(size_t)+AB_SIZE+*(size_t *)p, AB_FILL, AB_SIZE) ) ERROR(); /*... zmenit znacky... */ free( p); }
146
146 Next run podrobné spojování modulů až nakonec po direktivách typové konstrukce raději až po pointrech switch, for s příkladem systematicky bloky, vnořování, deklarace a inicializace, C / C++ / jazyky s bl. str. strlen postupně slajdy na argv int typy – size_t, ptrdiff_t, wchar_t Dvojrozměrné pole – jak ho alokovat Funkce printf Funkce random - pouziti Řetězce a argumenty příkazové řádky rozdělit do dvou přednášek 84-87, 109 – 116, 138-139 od Filipa pridat do slajdu callock
Podobné prezentace
© 2024 SlidePlayer.cz Inc.
All rights reserved.