530 likes | 683 Views
Podstawy informatyki 2013/2014. Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego Matuszyka. Przykład 1. int i=0, j=3; while (i<3 || j>-1) { cout<<i<<"t"<<j<<endl; i++; j--; }. 0 3
E N D
Podstawy informatyki2013/2014 Łukasz SztangretKatedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiałyDanuty Szeligi i Pawła Jerzego Matuszyka
Przykład 1 int i=0, j=3; while(i<3 || j>-1) { cout<<i<<"\t"<<j<<endl; i++; j--; } 0 3 1 2 2 1 3 0
Przykład 1 c.d. int i=0, j=3, ile_i=0, ile_j=0; while((ile_i++, i<3) || (ile_j++, j>-1)) { cout<<i<<"\t"<<j<<endl; i++; j--; } cout<<endl<<ile_i<<"\t"<<ile_j<<endl; 0 3 1 2 2 1 3 0 5 2
Przykład 2 int i=0, j=3, ile_i=0, ile_j=0; while((ile_i++, i<3) && (ile_j++, j>-1)) { cout<<i<<"\t"<<j<<endl; i++; j--; } cout<<endl<<ile_i<<"\t"<<ile_j<<endl; 0 3 1 2 2 1 4 3
Jakie wartości mają zmienne? #include<iostream> using namespace std; int main() { int a,b,c; a=2+2*2; b=3%2+1; c=4/2*2; } a = 6 b = 2 c = 4
Jakie wartości mają zmienne? #include<iostream> using namespace std; int main(){ int a,b,c,d; a = 1 ? 2,3 : 4,5; b = 0 ? 2,3 : 4,5; c = (1 ? 2,3 : 4,5); d = (0 ? 2,3 : 4,5); int e = 1 ? 2,3 : 4,5; int f = 0 ? 2,3 : 4,5; int g = (1 ? 2,3 : 4,5); int h = (0 ? 2,3 : 4,5); } a = 3 b = 4 c = 5 d = 5 BŁĄD BŁĄD g = 5 h = 5
Jakie wartości mają zmienne? #include<iostream> using namespace std; int main() { int a=2,b=1; a*=b+=2; } a = 6 b = 3
Jakie wartości mają zmienne (hex)? #include<iostream> using namespace std; int main() { short int a=0x1234, b, c; b=!a; c=~a; } a = 1234 b = 0 c = edcb
Jaką wartość ma zmienna? #include<iostream> usingnamespacestd; intmain() { char z; if (1==4>=3) z='T'; else z='F'; } z = 'T'
Co pojawi się na ekranie? #include<iostream> usingnamespacestd; intmain() { int cout=2; std::cout<<cout<<cout<<endl; std::cout<<(cout<<cout)<<endl; } 22 8
Funkcja • Podprogram – pozwala na definiowanie własnych „instrukcji”. Podprogramy w języku C++ nazywane są funkcjami. • Funkcja – podprogram o swojej własnej nazwie, który może być wywołany z innej części programu dowolną liczbę razy. • Składnia: zwracany_typnazwa(parametr1, parametr2) { … return zwracany_obiekt; }
Deklaracja funkcji • Każda funkcja musi być zadeklarowana • Parametry występujące w deklaracji mogą posiadać nazwy (ale nie muszą!): void f(double a); void f(double); • Uwaga: na końcu każdej deklaracji znajduje się średnik!!!
Deklaracja a definicja funkcji • Definicja funkcji jest równoznaczna z napisaniem jej treści. • Przed odwołaniem się do nazwy funkcji wymagana jest jej deklaracja (niekoniecznie definicja). • Sama funkcja może być zdefiniowana gdzie indziej – później albo wręcz w innym pliku. • Definicja jest jednocześnie deklaracją (ale nie na odwrót)
Deklaracja a definicja funkcji DEKLARACJA double pole(double); intmain() { double r=5,P; P=pole(r); } double pole(double r) { double P=3.14*r*r; return P; } WYWOŁANIE DEFINICJA
Deklaracja funkcji double pole(double); double objetosc(double,double); intmain() { double r=5,h=10,P,V; P=pole(r); V=objetosc(r,h); cout<<P<<endl<<V<<endl; } double pole(double r) { return 3.14*r*r; } double objetosc(double r, double h) { return pole(r)*h; } double pole(double); intmain() { double objetosc(double,double); double r=5,h=10,P,V; P=pole(r); V=objetosc(r,h); cout<<P<<endl<<V<<endl; } double pole(double r) { return 3.14*r*r; } double objetosc(double r, double h) { return pole(r)*h; }
Deklaracja funkcji intmain() { double pole(double); double objetosc(double,double); double r=5,h=10,P,V; P=pole(r); V=objetosc(r,h); cout<<P<<endl<<V<<endl; } double pole(double r) { return 3.14*r*r; } double objetosc(double r, double h) { return pole(r)*h; } intmain() { double pole(double); double objetosc(double,double); double r=5,h=10,P,V; P=pole(r); V=objetosc(r,h); cout<<P<<endl<<V<<endl; } double objetosc(double r, double h) { return pole(r)*h; } double pole(double r) { return 3.14*r*r; } BŁĄD
Funkcja lokalna intmain() { double pole(double); double r=5,P; P=pole(r); cout<<P<<endl; double pole(double r) { return 3.14*r*r; } } intmain() { double pole(double r) { return 3.14*r*r; } double r=5,P; P=pole(r); cout<<P<<endl; } • LOCAL FUNCTION DEFINITIONS ARE ILLEGAL
Deklaracja i definicja void f(int); intmain() { f(5); return 0; } void f(int a) { cout << a << endl; } void f(int a) { cout << a << endl; } intmain() { f(5); return 0; } Wiązanie na etapie kompilacji. Wczesne wiązanie.
Przykład późnego wiązania #include<iostream> usingnamespacestd; class pojazd { protected: intl_kol, max_sp; public: pojazd(inta,int b):l_kol(a),max_sp(b){} virtualvoid wypisz(); }; classsamochod: public pojazd { double poj_s; public: samochod(inta,intb,double c):pojazd(a,b), poj_s(c){} void wypisz(); }; intmain() { pojazd P(2,30),*W; samochod S(4,180,1.9); char a; cout<<"P czy S\n"; cin>>a; if (a=='P') W=&P; else W=&S; W->wypisz(); return 0; } Wiązanie na etapie wykonywania. Późne wiązanie.
Zwracanie wartości • Funkcja zwraca wartość przez instrukcję: return wyrażenie; • Wyrażenie musi być typu zgodnego z typem zwracanym przez funkcję lub dającego się skonwertować do typu zwracanego double pole(double r) { return 3.14*r*r; } wyrażenie typu double
Zwracanie wartości – funkcja void • Jeśli funkcja zwraca typ void, czyli nie zwraca niczego, to wewnątrz funkcji nie można użyć instrukcji return wyrażenie; a jedynie samą instrukcję return; • W funkcji może być kilka instrukcji return • Zawsze musimy zagwarantować, żeby każda ścieżka wykonania kończyła się odpowiednią instrukcją return.
Argumenty double objetosc(double,double); intmain() { double r=5,h=10,V1,V2; V1=objetosc(r,h); V2=objetosc(1.2,6.7); return 0; } double objetosc(double r,doubleh) { return 3.14*r*r*h; } argumenty aktualne argumenty formalne
Pusta lista argumentów Deklaracja funkcji: void f(); oznacza: • w języku C void f(…); • w języku C++ void f(void);
Przesyłanie argumentów przez wartość • Do funkcji przesyłana jest kopia obiektu (argumentu aktualnego) wywołania funkcji. • Wartość ta służy do inicjalizacji parametru formalnego. • Funkcja pracuje na kopii argumentu aktualnego. • Parametr formalny jest lokalną automatyczną zmienną o zakresie ważności funkcji => po opuszczeniu funkcji kopia znika, wobec tego znika też to, co zostało zrobione w funkcji na tej kopii.
Przesyłanie argumentów przez wartość void f(int); intmain() { int a=10; cout << a << endl; f(a); cout << a << endl; return 0; } void f(int a) { a*=2; cout << a << endl; } 10 a 10 20 a 10 10 20
Przesyłanie argumentów przez referencje • Argumenty można przesyłać do funkcji przez referencję. • Funkcja nie działa na kopii argumentu aktualnego, ale na referencji do zmiennej będącej argumentem aktualnym. • Zmiana wartości argumentu aktualnego przekazanego przez referencję jest widoczna w miejscu wywołania funkcji.
Przesyłanie argumentów przez referencje void f(int &); intmain() { int a=10; cout << a << endl; f(a); cout << a << endl; return 0; } void f(int &b) { b*=2; cout << b << endl; } 10 20 a b 10 20 20
Argumenty domniemane • Wszystkie lub część argumentów funkcji może posiadać wartości domniemane (domyślne) • Argumenty domniemane można wyspecyfikować tylko dla ostatnich parametrów funkcji. Jeśli podano argument domyślny dla pierwszego parametru pozostałe parametry także muszą mieć wartości domyślne. int potega(int P, int W=2); lub int potega(int P=1, int W=2); Zapis: int potega(int P=1, int W); generuje błąd.
Argumenty domniemane • Wyjątkiem jest rozszerzanie deklaracji: void f(int ,int ,int=1); void f(int ,int=5, int); void f(int=9, int, int); • Każdy argument domniemany można podać tylko raz, przy deklaracji funkcji. void f(int ,int=5, int=1); void f(int, int, int=2); BŁĄD
Argumenty domniemane a=1 b=2 c=3 void f(int=1 ,int=2 ,int=3); intmain() { f(); f(10); f(10,20); f(10,20,30); f(10, ,30); return 0; } void f(int a ,int b ,int c) { … } a=10 b=2 c=3 a=10 b=20 c=3 a=10 b=20 c=30 BŁĄD
Argumenty domniemane a=30 b=2 c=3 void f(int=1 ,int=2 ,int=3); intmain() { f((10,20,30)); f((10,20),30); f(10,(20,30)); (f(10,20,30)); return 0; } void f(int a ,int b ,int c) { … } a=20 b=30 c=3 a=10 b=30 c=3 a=10 b=20 c=30
Funkcja inline • Kompilator napotykając funkcję zdefiniowaną jako inline umieszcza treść tej funkcji tam, gdzie jest ona wywoływana. • Funkcje inline powinny być stosowane dla funkcji, których treść jest bardzo krótka i które wywoływane są w programie wiele razy. • Ostateczną decyzję o tym czy funkcja zostanie rozwinięta w miejscu wywołania podejmuje kompilator. Słowo kluczowe inline jest dla niego sugestią. • Jeśli funkcja jest oznaczona jako inline, to jej definicja musi być umiejscowiona przed jej pierwszym użyciem. Sama deklaracja nie wystarczy!
Zmienne lokalne statyczne • Zmienne globalne – zakres ważności: obszar pliku, są wstępnie inicjalizowane zerami. • Zmienne lokalne (automatyczne) – zakres ważności lokalny, nie są wstępnie inicjalizowane, zawierają „śmieci”. • Zmienne lokalne statyczne – są wstępnie inicjalizowane zerami i zajmują w pamięci ten sam obszar, co zmienne globalne. int a; intmain() { ... } intmain() { int a; ... }
Zmienne lokalne statyczne • Słowo kluczowe staticprzed zmienną wewnątrz funkcji pozwala na definiowanie zmiennej, która po zakończeniu działania funkcji nie jest „usuwana z pamięci” ale zachowuje swoje wartości do następnego wywołania funkcji. • Inicjalizacja zmiennej zadeklarowanej jako static dokonywana jest tylko raz, przy pierwszym wykonaniu tej funkcji.
Zmienne lokalne statyczne #include<iostream> using namespace std; void licznik(); int main() { for (int i=0; i<5; i++) licznik(); return 0; } void licznik() { int ile=0; ile++; cout << ile << endl; } 1 1 1 1 1
Zmienne lokalne statyczne #include<iostream> using namespace std; void licznik(); int main() { for (int i=0; i<5; i++) licznik(); return 0; } void licznik() { static int ile=0; ile++; cout << ile << endl; } 1 2 3 4 5
Rekurencja • Rekurencja (rekursja) – wywoływanie funkcji przez samą siebie • Jeśli problem rzeczywiście jest rekurencyjny, to program rozwiązujący go w sposób rekursywny jest przejrzysty. int silnia(int n) { ints=n; while (n-=1) s*=n; return s; } int silnia(int n) { if (n==0) return 1; return n*silnia(n-1); }
Preprocesor • To ta część środowiska programistycznego, która jest uruchamiana bezpośrednio przed uruchomieniem właściwego kompilatora. • Dokonuje pierwszego przeglądnięcia plików źródłowych. • Rozpoznaje dyrektywy i interpretując je, dokonuje odpowiednich działań ingerując w kod
Preprocesor • Dyrektywy są umieszczane w stosownych miejscach programu w osobnej linii programu. • Zaczynają się od znaku # (może on być poprzedzony białymi znakami), po którym następuje nazwa dyrektywy oraz ewentualnie argumenty: #<nazwa_dyrektywy> [args…] • Nie kończą się średnikiem! • Jeśli dyrektywa nie mieści się w jednej linii, oddzielamy linie znakiem \ (backslash).
Dyrektywa define • Postać: #define nazwa zastępczy_ciąg_znaków #define nazwa • Dyrektywa ta powoduje, że w kompilowanym pliku każde wystąpienie słowa nazwa będzie zastąpione podanym dalej ciągiem znaków. • Nazwa nie może zawierać białych znaków. • Białe znaki mogą pojawić się w zastępczym ciągu znaków.
Dyrektywa define #include<iostream> usingnamespacestd; #define PI 3.1416 intmain() { cout << PI*2*2 << endl; return 0; } const double PI=3.1416;
Dyrektywa undef • Postać: #undef nazwa • Występuje zawsze w parze z dyrektywą #define nazwa • Dyrektywa ta określa miejsce w pliku, od którego przestaje obowiązywać zdefiniowana wcześniej dyrektywa #define nazwa
Makrodefinicja #include<iostream> usingnamespacestd; #definewieksza(a,b) (a > b ? a : b) intmain() { int a=1,b=5; cout<<wieksza(a,b)<<endl; return 0; }
Funkcja inline #include<iostream> usingnamespacestd; inline intwieksza(int a,int b) { return (a>b?a:b); } intmain() { int a=1,b=5; cout<<wieksza(a,b)<<endl; return 0; }
Definevsinline #include<iostream> usingnamespacestd; inline int wieksza(int a,int b){ return (a>b?a:b);} intmain() { int a=1,b=5; cout<<wieksza(++a,++b)<<endl; cout<<a<<endl; cout<<b<<endl; return 0; } #include<iostream> usingnamespacestd; #definewieksza(a,b) (a>b?a:b) intmain() { int a=1,b=5; cout<<wieksza(++a,++b)<<endl; cout<<a<<endl; cout<<b<<endl; return 0; } 6 2 6 7 2 7
Definevsinline #include<iostream> usingnamespacestd; #define kwadrat(a) a*a intmain() { int a=2; cout<<kwadrat(a)<<endl; return 0; } #include<iostream> usingnamespacestd; #define kwadrat(a) a*a intmain() { int a=2,b=5; cout<<kwadrat(a+b)<<endl; return 0; } 17 4
Definevsinline #include<iostream> usingnamespacestd; inline int wieksza(int a,int b) { return (a>b?a:b); } #include<iostream> usingnamespacestd; #definewieksza(a,b) (a>b?a:b) Działa dla typu int Działa dla dowolnego typu Rozwiązanie: szablon funkcji
Kompilacja warunkowa • Czasem istnieje konieczność, aby pewne linie kody były kompilowane tylko wtedy, gdy będzie to konieczne, np.: • Debugowanie programu – wyświetlanie dodatkowych informacji, • Dołączanie pewnych fragmentów kodu zależy od środowisku pewnych nazw. • Kompilacja danego oznaczonego fragmentu kodu wykona się lub nie w zależności od spełnienia pewnych warunków.
Kompilacja warunkowa #include<iostream> usingnamespacestd; #define N 0 intmain() { int a=1,b=5; #if N cout<<a<<endl; #endif cout<<b<<endl; return 0; } #denife N 1 5 1 5
Dyrektywa include • Postać: #include <nazwa_pliku> #include "nazwa_pliku" • W dane miejsce w kompilowanym pliku źródłowym (w którym występuje ta dyrektywa) wstawiana jest zawartość pliku o zadanej nazwie. • Najczęściej używana dla wstawiania plików nagłówkowych (z definicjami typów, struktur danych, klas, itp.).