Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

Programování v jazyce C

Podobné prezentace


Prezentace na téma: "Programování v jazyce C"— Transkript prezentace:

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.)

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í 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

3 Obsah předmětu C++ C++ C C ZS LS Programování v jazyce C OOP (v C++)
Nejdůležitější: vlastní praxe Na přednáškách se nikdo nikdy programovat nenaučil

4 Obsah přednášky Přednáška Cvičení 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 Pascal vs. C++ Úvod do Programování, Programování  výuka (v) Pascalu
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 'Vše' již bylo naprogramováno
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 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 (3rd ed.) Bruce Eckel: Myslíme v jazyku C++ (Thinkinkg in C++ 2nd ed.) C++ In-depth aneb Jak správně C++ používat - pro ty, kdo již C++ nějak znají Scott Meyers: Effective C++ (2nd 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 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 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 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)

10 hello.c #include <stdio.h> #include <conio.h>
int main( int argc, char ** argv) { printf( "Hello\n"); getch(); return 0; }

11 hello.c #include <stdio.h> #include <conio.h>
direktiva preprocesoru vložení souboru deklarace knihovních funkcí #include <stdio.h> #include <conio.h> int main( int argc, char ** argv) { printf( "Hello\n"); getch(); return 0; } hlavička funkce název funkce typ návratové hodnoty formální parametry tělo funkce skutečné parametry příkaz volání funkce bez parametrů složené závorky BEGIN END

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 Překlad jednoho modulu a sestavení
knihovny standardní i jiné knihovní headery .h .obj .obj .obj .obj .obj .lib spustitelný program .cpp CC .obj Link .exe spojování (linkování) objektový modul (přeložený kód) kompilace

14 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

15 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 .obj .obj .c .obj .c .c další moduly

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 Integrované vývojové prostředí
.h .h .obj .obj .obj .obj .obj .lib .c .obj .c CC .obj .obj Link .exe .c editor debugger projekt

18 Make .h .h .obj .obj .obj .obj .obj .lib .c .obj .c CC .obj .obj Link
.exe .c makefile make

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 argumenty z příkazové řádky
Funkce vnořené funkce nelze! Základní programová jednotka je funkce Neexistují vnořené funkce Začátek programu – funkce main int fce2( int a) { int fce1( int x, int y) return x+y; } return fce1( 2*a+17); hlavička funkce 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) ... tělo funkce začátek programu argumenty z příkazové řádky později

21 návrat celočíselné hodnoty
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 void fce2( char* text) { printf( text); } void fce3( char* text) if( ! text) return; typ funkce int fce1( int x, int y) { return x+y; } návrat z funkce návrat celočíselné hodnoty

22 každý parametr musí mít typ
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

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 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 !!! deklarace celočíselných proměnných int fce( void) { int p; int q, r; int s = 5; int x, z = 0, y; return z + x; } deklarace s inicializací náhodná hodnota !!!

25 Příklad - malá násobilka
definice knihovních funkcí hlavička funkce, formální parametr #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); neinicializovaná proměnná inicializovaná celočíselná proměnná okanžitý návrat z funkce kontrola parametrů 2 * 7 = 14 začátek cyklu i++ konec cyklu konec, OK hlavní program konec, OK ignorování návratové hodnoty volání funkce se skutečným parametrem The END

26 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é'

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 globální proměnná formální parametr int g = 1; void fce( int x) { int z = 2; g = g + z * x; } int main( void) fce( 2); fce( 3); return g; lokální proměnná co vrátí main ?? skutečný parametr

28 test přiřazované hodnoty
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 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 syntakticky správně, ale dělá něco jiného
Podmíněný příkaz if (výraz) příkaz if (výraz) příkaz else příkaz if( a > 1) { b = 0; printf( "OK"); } else printf( "nee"); syntakticky správně, ale dělá něco jiného if( a > 1) printf( "OK"); if( a > 1) b = 0; printf( "OK"); if( a > 1) 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"); } syntakticky špatně

31 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"); if( a > 1) { if( b > 0) printf( "OK"); } else { printf( "nee"); } if( a > 1) { if( b > 0) printf( "OK"); else printf( "nee"); } U vnořených podmínek vždy používat { }

32 Vícenásobné větvení – konstrukce switch
zapomenutý break syntakticky OK, ale dělá něco jiného switch( errc) { case 0: b = 0; printf( "OK"); break; case -1: printf( "trochu spatne"); case -2: case -3: printf( "hodne spatne"); default: printf( "jina chyba"); } celočíselný výraz switch (výraz) { case konstanta: posloupnost příkazů break; default: } switch( errc) { case 0: b = 0; printf( "OK"); case -1: printf( "spatne"); break; } ukončení větve switch( ch) { case '0'..'9': x += ch - '0'; break; case 'A'..'F': x += ch - 'A'; } pokud výraz není roven žádné z konstant interval nelze

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 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);

34 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 ; } ekvivalent v jiném jazyce for( i=0; i<=9; i=i+1) { fce( i); } FOR I := 0 TO 9 DO FCE(I) i=0; while( i<=9) { fce( i); i=i+1; } jako inicializaci, podmínku i inkrement lze libovolný výraz výrazy 1,2,3 lze vynechat vynechání podmínky - nekonečný cyklus for( init(a); i<9 && a[i] >0; a[i++]=i*2) { fce( i); } for v C++: obecnější, širší možnosti použití

35 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; } C++ vyjimky - pozdeji

36 ukončil by pouze vnitřní cyklus, vnější cyklus by pokračoval
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ů nelze break - ukončil by pouze vnitřní cyklus, vnější cyklus by pokračoval 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(); label návěští C++ vyjimky - pozdeji

37 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 short 8 / 16 16 16 / 32 int 32 long 32 / 64 long long - 64 -2GB .. +2GB

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) char norma neurčuje signed / unsigned korektní porovnání na nerovnost pouze 'a' < 'ž' ? signed char unsigned char 'byte' wchar_t stddef.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 Výčtový typ 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 }; hodnoty doplní překladač (od 0) C: celočíselné konstanty - OK C++: samostatný typ - nelze test bitů explicitní hodnoty

40 'Reálné' typy float  double  long double Pozor!
Reálné výpočty jsou vždy nepřesné float  double  long double zvýšená přesnost double x = 1; double y = x / 3; if( x == 3 * y) printf( "Presne"); else printf( "Nepresne"); malá přesnost - nepoužívat! standard pro 'reálné' výpočty pro přesné hodnoty používejte přesné typy raději nepoužívat pouze pro fyzikální nebo numerické veličiny double zustatek = 15.60; long zustatek_v_halerich = 1560;

41 Číselné konverze Automatické konverze (integral promotions)
vždy když je použit menší typ než int 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 při nestejných operandech

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 * / % multiplikativní operátory + - aditivní operátory << >> bitové posuny L < <= > >= uspořádání == != rovnosti & bitové AND ^ bitové XOR | bitové OR && booleovské AND || booleovské OR ? : podmíněný výraz P = *= /= %= += -= &= ^= |= <<= >>= přiřazení kombinované přiřazení , sekvence

43 Základní aritmetické operátory
+ - * / % podle typu operandů  automatická konverze na větší typ % - modulo int x=5, y=3; double a=5, b=3; modulo je pouze celočíselná operace celočíselné dělení x / y 1 x / b 1.666 x % y 2 x % b Error a / b a / y a % b a % y reálné dělení

44 Bitové a logické operátory
& | ^ ~ - bitové operace AND, OR, XOR, NOT && || ! - logické operace AND, OR, NOT oba op.  0 5 & 3 1 5 && 3 5 | 3 7 5 || 3 5 ^ 3 6 5 ^^ 3 Error 5 ^ 15 10 5 & 0 5 && 0 5 | 0 5 5 || 0 5 = 01012 3 = 00112 1 = 00012 7 = 01112 9 = 10012 15 = 11112 10 = 10102 alespoň jeden operand  0 neexistuje ^ zobak nad '6' | 'bar' nad '\' ~ vlnka nad '`' alespoň jeden operand = 0

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í ! test mezí pole před přístupem k prvku pole int x[10]; // pole 0..9 if( i < 10 && x[i] != 0) y = y / x[i]; if( x==y && *x++) ... Pozor! operátory s vedlejšími efekty se nemusí provést ! POZOR!!! Přiřazení! (zde hodnota vždy = 1) if( x = 1) ...

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 Podmíněný výraz, sekvence
ternární operátor 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 if (y>0) x =y; else x = 0; x = (y>0 ? y : 0); operátor sekvence ('zapomnění') x = (tmp = y, y = y + 1, tmp); ekvivalent x = y++;

48 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; i = 0; p[ i++] = i++;

49 Fungující triky vs. chyby
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 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; 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; 1 2 3 4 ??? ? 5 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

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 Ukazatele 1 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á je nějak velká (typ) má hodnotu 1 někde bydlí

53 Ukazatele p: x: 17 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 p: x: 17

54 přepsání náhodného místa v paměti
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?

55 Ukazatele - příklad x: 1 :px y: 3 :py 0: 7 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: 3 :py umožní test 0: 7 typicky 'segmentation fault' váš program bude ukončen pro pokus o porušení ochrany paměti

56 přístup k hodnotě proměnné přes ukazatel
Ukazatele - příklad x: 1 :px 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; y: 3 :py x: 1 :px y: 4 :py Pozor na prioritu! *py++  *(py++) přístup k hodnotě proměnné přes ukazatel

57 Ukazatele - příklad x: 1 :px y: 1 :py x: 9 :px y: 1 :py
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

58 Pole a ukazatele, aritmetika ukazatelů
1 2 3 4 ? 20 int a[5]; int *p; a[2] = 20; p = &a[2]; a[0] = *p - 15; p++; *p = 30; a p reference na prvek pole 5 ? 20 30 p inkrementace ukazatele posun na daší prvek

59 Pole a ukazatele, aritmetika ukazatelů
5 ? 20 30 40 p = &a[1]; *(p+3) = 40; Operátor [] p[i]  *(p+i) &p[i]  p+i a  &a[0] p přičtení čísla k ukazateli  posun o n prvků 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]

60 Pole a ukazatele, aritmetika ukazatelů
int a[5]; int *p; 10 20 30 40 nekompatibilní typy nestejná úroveň indirekce 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; identifikátor pole je konstantní  nelze do něj přiřazovat p[i]  *(p+i)  *(i+p)  i[p]

61 Řetězce Jazyk C++ nezná pojem řetězec (!) – konvence, knihovny
Řetězec je pole znaků (char) zakončené nulou "Ahoj" X proměnná 'X' znaková konstanta - celočíselná hodnota "X" řetězec - ukazatel Každý řetězec musí být vždy ukončen nulou 'A' 'h' 'o' 'j' '\0' '\0' = 0 char buffer[4]; strcpy( buffer, "Ahoj"); 'A' 'h' 'o' 'j' '\0' pozor na uvozovky a apostrofy ! vždy myslet na koncovou nulu ! kód znaku v použitém kódování (ASCII, CP1250, ISO , 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 'Řetězcové proměnné' char s1[] = "Uno"; const char *s2 = "Due";
Inicializované pole (konstantní ukazatel) s1++ nelze! s1: 'U' 'n' 'o' '\0' s2: 'D' 'u' 'e' '\0' Inicializovaná proměnná typu ukazatel s2++ se přesune na další znak anonymní globální proměnná const char[]

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 <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 ?

64 Délka řetězce – různé způsoby implementace
podmínka for cyklu může být libovolná přístup přes index int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; více inkrementací prázdné tělo nezapomenout na ';' !! for( i=0; *s != '\0'; i++) s++; for( i=0; *s != '\0'; i++, s++) ; složitější podmínka: test nenulovosti inkrementace ukazatele int i=0; while ( *s++ != '\0') i++; int i = 0; while ( *s != '\0') { i++; s++; } return i; while(a!=0)  while(a) podmínka je splněna pokud je nenulová int i=0; while ( *s++) i++; rozdíl ukazatelů = počet prvků mezi nimi pozor na ± 1 ! char *p = s; while (*p++) ; return p-s-1; přístup přes ukazatel

65 Řetězce - kopírování buf pozdrav
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); kopíruje pouze do koncové '\0' buf A h o j \0 ? pozdrav A h o j \0

66 Řetězce – chyby při kopírování
vždy pozor na dostatek místa funkce nic nekontroluje !!! váš program provedl... char buf[6]; char pozdrav[] = "Dobry den"; strcpy( buf, pozdrav); buf D o b r y d e n \0 pozdrav D o b r y d e n \0 kopírování na neinicializovaný ukazatel !!! váš program provedl... char *ptr; char pozdrav[] = "Ahoj"; strcpy( ptr, pozdrav); ? ptr A h o j \0 pozdrav ptr neinicializovaný !!! A h o j \0

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"); po strcpy A h o j \0 ? po strcat 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

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 výsledek podle prvního rozdílného znaku co znamená 's1 < s2'? A B C E = D

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 <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); %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 Parametry příkazové řádky
C:\> myprog.exe -n -w a.txt b.txt pole řetězců (ukazatelů na char) int main( int argc, char** argv) argv 5 argc b . t x \0 a . t x \0 Počet parametrů včetně názvu programu ! = počet ukazatelů v argv - w \0 - n \0 m y p r o g . e x \0

71 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 výstup řetězce a odřádkování posun na další parametr

72 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 b . t x \0 *argv  argv[0] typ: char* a . t x \0 - w \0 argv typ: char** - n \0 m y p r o g . e x \0 argv **argv  argv[0][0] typ: char

73 Zpracování příkazové řádky
int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; } b . t x \0 a . t x \0 argv++ **argv - w \0 - n \0 m y p r o g . e x \0 argv

74 Zpracování příkazové řádky
int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); argv++; } b . t x \0 a . t x \0 *argv == 0 - w \0 - n \0 m y p r o g . e x \0 argv

75 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 b . t x \0 a . t x \0 - w \0 výkonná funkce - n \0 argv p r g . e x \0

76 Dynamická alokace paměti
Velikost datového typu sizeof(char) = 1 vždy C: standardní knihovny <malloc.h> void* malloc( int size); void free( void* p); 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'; s: X h o j \0 vždy ověřit !!! váš program provedl...

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 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 Organizace paměti procesu – 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 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 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 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 = * 3; ...

83 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í C++: raději skrýt do třídy inicializace: C: před vstupem do main C++: před prvním průchodem

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 Struktury struct osoba { char jmeno[20]; char prijemni[30];
definice 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 = 1; položky struktury struktura, ukazatel na strukturu, pole struktur definice proměnné typu struktura s inicializací přístup k položkám (*x).y  x->y

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 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

88 Kombinace typových kostrukcí - příklad
int*(*pf[10])(void); int*(*maso(int*(*p1)(void),int*(*p2)(void)))(void); co to je za maso ???

89 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

90 Souborový vstup a výstup
deskriptor souboru - deklaruje programátor struktura definovaná v <stdio.h> FILE * FILE Neznámý obsah Pro knihovní funkce OS 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)

91 (ukazatel na strukturu)
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 '\\' !!! čtení ze souboru zavření souboru

92 r: soubor musí existovat
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 ... r: soubor musí existovat mode soubor ex. soubor neex. seek r R Error w Del, W W a End r+ R/W w+ Del, R/W a+ w: soubor se smaže a: otevřít na konci +: vždy čtení i zápis

93 Textové vs. binární soubory
Textový soubor konverze konců řádek ('\n') na platformě 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 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); pro priste udelat obrazek na fread/fwrite

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); Nepoužívat! Nelze ohlídat přetečení bufferu všechny souborové funkce lze použít i pro std. v/v jednotný zápis na std výstup nebo do souboru

96 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> time, gmtime, strftime, asctime, clock, ... pro priste udelat obrazek na fread/fwrite ... a mnoho mnoho dalších

97 Problém v C: české znaky, ale i jiné diaktické znaky
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 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 pro priste udelat obrazek na fread/fwrite

98 Uživatelsky napsaná třídící funkce. Návratové
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 ); } 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, a > 0. Ignore case Parametry: pole k setřídění, počet prvků pole, velikost 1 prvku, porovánací funkce pro priste udelat obrazek na fread/fwrite

99 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

100 Spojování modulů – problém
x.c double A() { return B( 7); } y.c double B() { return 3.14; } error: Undefined 'B'

101 Spojování modulů – externí deklarace
x.c double B(); double A() { return B(); } y.c double B() { return 3.14; }

102 Spojování modulů – nekonzistence
x.c double B(); double A() { return B(); } y.c int B( int q) { return q+1; } nekonzistence funkce B (počet a typy parametrů, návratová hodnota) x.obj import B export A y.obj export B app.exe C: nedefinované chování C++: linker error

103 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; } int B( int q); double A() { return B( 7); } preprocesor hlavičkový soubor (header)

104 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; } nekonzistence int B( int q); double A() { return B( 7); }

105 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'

106 Spojování modulů – duplicitní data
x.c #include "y.h" double A() {} y.h int c; 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'

107 Deklarace vs. definice x.c y.h y.c x.obj y.obj
#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 Definice Deklarace Deklarace x.obj import c export A y.obj export c

108 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;

109 Spojování modulů - duplicita typů
Přes y.h a z.h je t.h vložen dvakrát x.c #include "y.h" #include "z.h" double A() { return C+D; } t.h enum T { P, Q}; y.h #include "t.h" extern enum T C; error: Type redefinition: 'T' enum T { P, Q}; extern enum T C; enum T { P, Q}; extern enum T D; double A() { return C + D; } z.h #include "t.h" extern enum T D;

110 Spojování modulů - #ifndef
není-li symbol definován ... 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 definice nového symbolu (makra) y.h #include "t.h" extern enum T C; #ifndef _T_H #define _T_H enum T { P, Q}; #endif extern enum T C; extern enum T D; symbol již definován z.h #include "t.h" extern enum T D; nepřekládá se

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 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 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 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 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 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 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 int x; char * p = (char *)&x; struct { char a; int b; } S; fwrite( &S, 1, sizeof( S), fp); #ifdef _MSC_VER // Microsoft typedef __int64 INT64; const char delimiter = '\\'; #else typedef long long INT64; #ifdef UNIX const char delimiter = '/'; #endif Ideál: Program přenositelný bez úpravy zdrojového textu

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 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 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 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>

122 Dynamické seznamy ukazatel na další ukazatel na další hlava seznamu
struct osoba { char jmeno[20]; int narozen; osoba *dalsi; }; osoba *zamestnanci; ukazatel na další ukazatel na další hlava seznamu hlava seznamu jmeno Novak narozen 1905 dalsi Jason 1948 Drson 1990 konec seznamu

123 Vyhledání prvku zamestnanci os přechod na další prvek pozor na konec!
osoba *os; for( os=zamestnanci; os; os=os->dalsi) { if( os->narozen == 1972) return os; } return 0; přechod na další prvek pozor na konec! zamestnanci os lezu po krabicích Novak 1905 Jason 1948 Karel 1972 Drson 1990

124 Přídání prvku na začátek
nový prvek osoba *zamestnanci; ... osoba *novy; if( ! (novy = malloc(sizeof(osoba)))) error(); strcpy( novy->jmeno, "Novak"); novy->narozen = 1905; novy->dalsi = zamestnanci; zamestnanci = novy; zařazení do seznamu zamestnanci Jason 1948 Drson 1990 novy Novak 1905

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; zamestnanci sem novy přepojení ukazatelů Karel 1972 Novak 1905 Jason 1948 Drson 1990

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); prvni: posledni: prvek prvek prvek 1 2 3 ukazatel na další poslední prvek nulový ukazatel

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 Oblíbené chyby – struktura programu
1. Chybějící nebo přebývající středník: for (i=0; i<n; i++); { ... } funkce(); { ... } 2. Přehozené parametry funkcí nebo části konstrukce: char text[20]; strcpy( "Chyba!", text); for( i=0; i++; i<20)... 3. Nezapomínat na break v konstrukci switch 4. 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 5. K čemu patří else? if(podmínka1)   if(podmínka2) /* akce */ else /* jiná akce */

129 Oblíbené chyby – výrazy
6. Celočíselné dělení: x=1/3; 7. Zaokrouhlovací chyby: for (x=0; x!=1.0; x+=0.1)... 8. Odporující si parametry u printf: printf("%d",1.25); 9. Záměna & | a && || a=1; b=2; if(a&b)... 10. Zkrácené vyhodnocování logických výrazů 11. Pozor na záměnu znaků: a=1; if (a<1,5)... 12. Pozor na prioritu operátorů. Závorka navíc nikdy neuškodí. 13. 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 Oblíbené chyby – ukazatele
14. Neinicializované proměnné, zvl. ukazatele: char *text; strcpy( text, "Chyba!"); 15. Ukazatel na proměnnou, která už neexistuje: char text[20]; strcpy( text, ....); return text; 16. Chybějící nebo naopak přebývající & u parametru předávaného ukazatelem: scanf( "%d %s", i, &text);

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 ==================

133 Nepoužité slajdy

134 int printf( const char *, ...);
Preprocesor stdio.h /*...*/ int printf( const char *, ...); /*...*/ int printf( const char *, ...); int getch(); int putch(); /*...*/ int main( int argc, char ** argv) { printf( “Hello\n”); getch(); return 0; } conio.h int getch(); int putch(); /*...*/ hello.c #include <stdio.h> #include <conio.h> int main( int argc, char ** argv) { printf( “Hello\n”); getch(); return 0;

135 Typy souborů Microsoft Unix .c Zdrojový kód C .cpp .C .cc .cpp
.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 .o Objektový modul .lib .a Knihovna .dll .so Dynamicky linkovaná knihovna .exe bez přípony Spustitelný program

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 encapsulation zapouzdření inheritance dědičnost polymorphysm polymorfismus

137 Výhody a nevýhody jazyka C
přenositelnost standardní součást UNIXu dědictví UNIXu úsporná syntaxe nepř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 Váňovo paradoxon čím vyšší jazyk tím méně přenositelný
Zajímavé vlastnosti C Váňovo paradoxon čím vyšší jazyk tím méně přenositelný 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

139 Přeložený kód - modul hello.obj Code Data ... push call ret
Export _main Import _printf _getch

140 Spojování modulů cstart.obj hello.obj printer.obj Code Code Code ...
call ... call ... Export Export Export entry point _main _printf moduly - pro knihovny malilinkate - aby linker bral jen to co potrebuje Import Import Import _main _printf _getch

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; } Globální buf: "abc" "ahoj" Lokální zásobník c: ??? s: Dynamická heap "ahoj" lokální ukazatel na dynamická data

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'); Globální buf: "abc" "ahoj" Lokální zásobník str: ??? c: 'X' s: Dynamická heap "Xhoj" str zatím není inicializované

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'); Globální buf: "abc" "ahoj" Lokální zásobník str: Dynamická heap "Xhoj" návrat ukazatele na dynamická data

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"); x->y  (*x).y Pozor! *x.y  *(x.y)

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 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, od Filipa pridat do slajdu callock


Stáhnout ppt "Programování v jazyce C"

Podobné prezentace


Reklamy Google