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í na základě přípravy napsání programu na PC s moţností pouţít domácí úkoly (povinné i dobrovolné) ţádná další literatura (s výjimkou nápovědy) není povolena 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 p; int q, r; int s = 5; int x, z = 0, y; 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
Čí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 < <= > >= 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 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); operátor sekvence ('zapomnění') if (y>0) x =y; else x = 0; a, b 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 ("%+05d \n%.2f \n", 7, r + 7); printf ("Retezec %s zacina na %c \n", buf, buf[0]); printf ("Hodnota 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] typ: char* argv[4][1] typ: char *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