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