programov ání v jazyce c
DESCRIPTION
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.) [email protected] http://kocour.ms.mff.cuni.cz/~lansky/. Studijní povinnosti. Zápočet - PowerPoint PPT PresentationTRANSCRIPT
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.)
http://kocour.ms.mff.cuni.cz/~lansky/
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
Programování v jazyce C OOP (v C++) ZS LS
Obsah předmětu
C++
C C
C++
Nejdůležitější: vlastní praxeNa přednáškách se nikdo nikdy programovat
nenaučil
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
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
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án
o
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 UNIXem1973 přepsání jádra UNIXu do C1978 Kerninghan, Ritchie: The C Programming Language1980 standardy – ANSI X3J11, od r. 1999 ISO 98991980 AT&T - "C with Classes"1983 poprvé název C++ (Rick Mascitti)1985 Stroustrup: The C++ Programming Language1989 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ětImplementace 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>
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í parametr
y
název funkce
typ návratové hodnoty
skutečné parametr
yvolá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í
.cpp
.h
CC .obj Link .exe
.obj.obj.obj.obj.obj.lib
kompilacespojování(linkování
)
knihovnystandardní i
jiné
knihovní headery
objektový modul
(přeložený kód)
spustitelný
program
14
Modul - ucelená funkčí jednotkamodul.cpp (.c) - implementacemodul.h - definice rozhraní
Oddělený překlad - dělení na moduly
fotbal.cpp
fotbal.h
hriste.cpp hrac.cpp mic.cpp
hriste.h hrac.h mic.h
rozdělení projektu do modulů
a vytváření headerů je umění,
nedá se to naučit na přednášce
15
Překlad více modulů – oddělený překlad
.c
.h .h
CC .obj Link .exe
.obj.obj.obj.obj.obj.lib
.obj.obj.obj
kompilace jednoho modulu
knihovnyknihovní headery
vlastní headery
.c
.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í
.c
.h .h
CC Link .exe
.obj.obj.obj.obj.obj.lib
.obj.obj.obj.c
.c
debugger
projekt
editor
18
Make
.c
.h .h
CC Link .exe
.obj.obj.obj.obj.obj.lib
.obj.obj.obj.c
.c
makemakefile
19
Program a modul
Program v C++ nemá (vnořovanou) blokovou strukturuNeexistuje "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
Funkce
Základní programová jednotka je funkceNeexistují vnořené funkceZačá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
Funkce - návratový typ
Typ funkce = typ návratové hodnotyHodnota 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
Parametry funkce
Pevný počet, pevný typ možnost proměnného počtu parametrů - printf
Parametry jsou odděleny čárkouU každého parametru musí být uveden jeho typFunkce 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
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 !!!
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
Příklad - malá násobilka
#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;}
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
konec, OK
hlavní program
ignorování návratové hodnoty volání funkce
se skutečným
parametremThe 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
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
Globální proměnné
Definice mimo tělo funkceViditelné v jakékoliv funkciMožná inicializace – při startu programuPoužívat s rozvahou!!
pouze pro sdílená dataint 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
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á hodnotaVý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
Podmíněný příkaz
if (výraz) příkazif (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
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
Vícenásobné větvení – konstrukce switch
switch (výraz) { case konstanta: posloupnost příkazů break; case konstanta: 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
Cyklus – konstrukce while a do-while
while (výraz) příkaz podmínka na začátkudo 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
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ínka inkrement
tělo cyklu
FOR I := 0 TO 9 DO FCE(I)
ekvivalent v jiném jazyce
for( init(a); i<9 && 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
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
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
Nepoužívat! .... pokud k tomu není dobrý důvodKdyž 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
labelnávěští
37
Celočíselné typy
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
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 longsize_t, ptrdiff_t, wchar_t
-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 / unsignedkorektní porovnání na nerovnost pouze 0 .. 127'a' < 'ž' ?
signed char -128 .. 127unsigned char 0 .. 255 - 'byte'wchar_t stddef.h: znak rozšířené sady (Unicode)
časté použití:test
(ne)nulovostinapř. ukazatelů
záleží na implementaci
většinou char = signed
40 > 200 !!!
200 -56
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
'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žívatpouze 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
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
Přehled operátorů, asociativita a priorita
postfi
x
++ --( )[ ]-> .::
post-in/de-krementacevolání funkceindexpřístup k položce strukturykvalifikace identifikátoru
pre
fix
++ --! ~+ -& *sizeof( )newdelete
pre-in/de-krementacebooleovská a bitová negaceunární +/-reference, dereferenceměření velikostipřetypovánídynamická alokacedynamická 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
Základní aritmetické operátory
+ - * / % podle typu operandů automatická konverze na větší typ % - modulo
x / y 1 x / b 1.666
x % y 2 x % b Error
a / b 1.666 a / y 1.666
a % b Error a % y Error
int x=5, y=3;
double a=5, b=3; modulo je pouze celočíselná
operaceceločíselné dělení
reálné dělení
44
Bitové a logické operátory
& | ^ ~ - bitové operace AND, OR, XOR, NOT&& || ! - logické operace AND, OR, NOT
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
neexistuje
5 = 01012
3 = 00112
1 = 00012
7 = 01112
9 = 10012
15 = 11112
10 = 10102
oba op. 0
alespoň jeden
operand 0
alespoň jedenoperand = 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í !
int x[10]; // pole 0..9
if( i < 10 && x[i] != 0) y = y / x[i];
test mezí pole předpří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
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
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);
ekvivalentx = y++;
ternární operátor
operátor sekvence
('zapomnění')
48
i = 0;p[ i++] = i++;
Pravidla vyhodnocování
a( b,c)vedlejší efekty parametrů jsou vyhodnoceny před zavoláním fcea && b je-li a nulový, b se nevyhodnocujea || b je-li a nenulový, b se nevyhodnocujea ? b : c po vyhodnocení a se vyhodnotí buď b nebo ca , 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
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
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;
0 1 2 3 4 ???
? 1 2 3 4 5
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
poleo souřadnicích m a n
čárka = operátor sekvence
význam:m-prvkové pole
typu(n-prvkové pole
typu t)
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
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á
1někde bydlí
je nějak velká (typ)
má hodnot
u
53
Ukazatele
Deklarace:t x – proměnná x typu tt *p - ukazatel p na typ t
Operátor reference: p = &xOperátor dereference: x = *p
Neinicializovaný ukazatel vs. nulový ukazatel C: #define NULL 0 C++: 0
17x:
p:
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
1x: :px
3y: :py
?
? 5
jaký zde bude obsah x a y?
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
1x: :px
3y: :py
0: 7
umožní test
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
1x: :px
4y: :pyPozor na prioritu!
*py++ *(py++)
1x: :px
3y: :py
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;
1x: :px
1y: :py
9x: :px
1y: :pyjaký zde bude obsah x a y?
58
Pole a ukazatele, aritmetika ukazatelů
int a[5];int *p;
a[2] = 20;p = &a[2];
a[0] = *p - 15;p++;*p = 30;
0 1 2 3 4
? ? 20 ? ?
5 ? 20 30 ?
p
p
reference na prvek pole
inkrementace ukazatele
posun na daší prvek
a
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 ? 20 30 40
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-ukazatelJe-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;
identifikátor pole je konstantní nelze do něj přiřazovat
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];
a[4] = p + 1;p = a[0];p[1] = a;
a = p + 2;
nekompatibilní typynestejná úroveň
indirekce
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
'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
'Ř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
Ř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);
A h o j \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
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 nenulovostiinkrementace
ukazatele
while(a!=0) while(a)
podmínka je splněnapokud je nenulová
rozdíl ukazatelů = počet prvků mezi
nimi pozor na ± 1 !
65
Řetězce - kopírování
char* strcpy( char* d, const char* s);
zkopíruje obsah sdo místa začínajího od
dchar buf[8];char pozdrav[] = "Ahoj";strcpy( buf, pozdrav);
A h o j \0pozdrav
A h o j \0 ? ? ?buf
kopíruje pouze do koncové '\0'
66
Řetězce – chyby při kopírování
D o b r y d e n \0pozdrav
buf
char buf[6];char pozdrav[] = "Dobry den";strcpy( buf, pozdrav);
D o b r y d e n \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...
A h o j \0pozdrav
ptr?
A h o j \0
ptr neinicializovaný !!!
67
Řetězce – zřetězení, vyhledávání
char* strcat( char* d, const char* s); připojí s za dchar* strchr( const char* s1, int c); vyhledá první pozici c v s1char* 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");
A h o j \0
? ? ? ? ?po strcpy
A h o j B a b i \0
?po strcat
buf
bp
A h o j B a b i \0
?pozor na dostatek
místa !
68
Řetězce – porovnávání a další funkce
int strcmp( const char* s1, const char* s2);s1 < s2 -1s1 = s2 0s1 > 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'? A B C E
= = =
A B C D E
výsledek podle prvního rozdílného
znaku
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 ("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
Parametry příkazové řádky
C:\> myprog.exe -n -w a.txt b.txt
0
m y p r o g . e x e \0
- n \0
- w \0
a . t x t \0
b . t x t \0
argv
5argc
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
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-wa.txtb.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++; }}
m y p r o g . e x e \0
- n \0
- w \0
a . t x t \0
b . t x t \0
argv
0
argvtyp:
char**
**argv argv[0][0]
typ: char
argv[4][1]
*argv argv[0]
typ: char*
argv[4]
73
Zpracování příkazové řádky
int main( int argc, char** argv){ while( *argv) { printf( "%s\n", *argv); argv++; }}
m y p r o g . e x e \0
- n \0
- w \0
a . t x t \0
b . t x t \0
argv
0
argv++ **argv
74
Zpracování příkazové řádky
int main( int argc, char** argv){ while( *argv) { printf( "%s\n", *argv); argv++; }}
m y p r o g . e x e \0
- n \0
- w \0
a . t x t \0
b . t x t \0
argv
0
*argv == 0
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
p r g . e x e \0
- n \0
- w \0
a . t x t \0
b . t x t \0
0
argv
76
Dynamická alokace paměti
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
char* s;s = malloc( 20);if( ! s) error();strcpy( s, "ahoj");*s = 'X';
s:
X h o j \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 typusizeof(char) = 1 vždy
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
R0R1...
SP
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
R0R1...
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
R0R1...
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
R0R1...
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
R0R1...
SP
{ char pole[100]; char s[] = "Ahoj"; int x = 1 + 2 * 3; ...
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 mainC++: před prvním průchodem
C++: lepší řešení - namespace
C++: raději skrýt do třídy
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];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
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 * xconst 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 & xconst 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
čtení deklarací: od identifikátoru doprava,
až to nepůjde, tak doleva
typicky se nepoužívápole = ukazatel na 1.
prvek
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
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
Souborový vstup a výstup
struktura definovaná
v <stdio.h>
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
#include <stdio.h>
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 soubor
u
zavření souboru
pozor na '\\' !!!
92
Otevření souboru
FILE* fopen( const char* fname, const char* mode);
r open file for readingw truncate to zero length or create file for writinga append; open or create file for writing at end-of-filer+ open file for update (reading and writing)w+ truncate to zero length or create file for updatea+ append; open or create f. for upd., writing at end-of-file
rb binary file... mod
esoubor ex.
soubor neex.
seek
r R Error 0
w Del, W W 0
a W W End
r+ R/W Error 0
w+ Del, R/W R/W 0
a+ R/W R/W End
+: vždy čtení i zápis
a: otevřít na konci
r: soubor musí existovat
w: soubor se smaže
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
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
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ápisna std výstup nebo
do souboru
Nepoužívat!Nelze ohlídat přetečení
bufferu
všechny souborové
funkce lze použíti pro std. v/v
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, ungetcFILE, 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, ...
... a mnoho mnoho dalších
97
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í znakyisalpha – 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ísmenotoupper – k zadanému písmenu vrací příslušné velké písmeno
Problém v C: české znaky, ale i jiné diaktické znaky
98
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.
Parametry: pole k setřídění, počet prvků pole, velikost 1 prvku, porovánací funkce
Ignore case
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 makraraději vůbec nepoužívat, lépe inline
funkcetest na (ne)definovanost symbolu
obrana před vícenásobným #includeviz 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
x.c
double A() { return B( 7);}
Spojování modulů – problém
error:Undefined 'B'
y.c
double B() { return 3.14;
}
101
x.cdouble B();double A() { return B();}
Spojování modulů – externí deklarace
externí deklarace
y.c
double B() { return 3.14;
}
102
Spojování modulů – nekonzistence
C: nedefinované chování
C++: linker error
x.cdouble B();double A() { return B();}
y.c
int B( int q) { return q+1; }
x.obj
import Bexport A
y.obj
export Bapp.exe
nekonzistence funkce B
(počet a typy parametrů, návratová hodnota)
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
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
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
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;int c;
x.obj
export c export A
linker error: Duplicate symbol 'c'
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.objimport c export A
Deklarace
Definice
Deklarace
Definice
108
Spojování modulů - typy
x.c#include "y.h"double A() { return C; }
y.c#include "y.h"
enum T C;
y.henum 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 typuteď není nutné chápat přesný význam Deklarace proměnné tohoto typu
109
Spojování modulů - duplicita typů
x.c#include "y.h"#include "z.h"double A() { return C+D; }
t.henum 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.hje t.h vložen dvakrát
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_Henum T { P, Q};#endif
#ifndef _T_H#define _T_Henum T { P, Q};#endifextern enum T C;
#ifndef _T_H#define _T_Henum T { P, Q};#endifextern 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
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
#ifdef _MSC_VER // Microsoft typedef __int64 INT64; const char delimiter = '\\'; #else typedef long long INT64; #ifdef UNIX const char delimiter = '/'; #else const char delimiter = '\\'; #endif#endif
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
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
struct osoba { char jmeno[20]; int narozen; osoba *dalsi;};
osoba *zamestnanci;
hlava seznamu
jmeno Novak
narozen 1905
dalsi
Jason
1948
Drson
1990
ukazatel na další
konec seznam
u
hlava seznamu
ukazatel na další
123
Vyhledání prvku
osoba *os;for( os=zamestnanci; os; os=os->dalsi) { if( os->narozen == 1972) return os;}return 0;
zamestnanci os
Karel
1972
Novak
1905
Jason
1948
Drson
1990
přechod na další prvek
pozorna konec!
lezu po krabicích
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
zamestnancizařazení do seznamu
novy
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
zamestnanci sem Karel
1972
novy přepojení
ukazatelů
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ší
2 3
prvni:
posledni:
fronta
prvek prvek prvek
poslední prveknulový ukazatel
127
Další dynamické struktury
obousměrně propojený lineární seznambinární strom, vyvážený stromobecný strom, obecný grafgumové poleasociativní pole
mnoho DS ve standardních knihovnách - C++ STL
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
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
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
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
Preprocesor
/*...*/int printf( const char *, ...);/*...*/
int getch();int putch();/*...*/
#include <stdio.h>#include <conio.h>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
Typy souborů
Microsoft Unix
.c .c Zdrojový kód C
.cpp .C .cc .cpp Zdrojový kód C++
.h .h Hlavičkový soubor C
.h .hppnebo bez přípony
.h .H .hh .hppnebo bez přípony
Hlavičkový soubor C++
.i .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čnostpolymorphysm 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 řezničiny
138
Zajímavé vlastnosti C
Přenositelnost velikost int, direktivy
Zahození hodnotyPří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
Přeložený kód - modul
hello.obj
...pushcall...call...ret
_main
_printf_getch
'H', 'e', 'l', 'l', 'o', 10, 0Data
Export
Import
Code
140
Spojování modulů
hello.ob
j...callcall...
_main
_printf_getch
Export
Import
Code cstart.o
bj...call...
entry point
_main
Export
Import
Code printer.o
bj...
_printfExport
Import
Code
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:
s:
lokální ukazatelna 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');
"abc""ahoj"
'X'
"Xhoj"
Dynamickáheap
Globální
buf:
c:
s:
str zatím není
inicializované
str:
Lokálnízásobník
???
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 ukazatelena dynamická data
str:
Lokálnízásobník
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
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áchtypové konstrukce raději až po pointrechswitch, for s příklademsystematicky bloky, vnořování, deklarace a inicializace, C / C++ / jazyky s bl. str.strlen postupněslajdy na argvint typy – size_t, ptrdiff_t, wchar_t
Dvojrozměrné pole – jak ho alokovatFunkce printfFunkce random - pouzitiŘetězce a argumenty příkazové řádky rozdělit do dvou přednášek84-87, 109 – 116, 138-139 od Filipa pridat do slajducallock