Przekazywanie argumentów wskaźniki klasyczne wywołanie wyliczenie i zwrotne przekazanie tylko jednej wielkości moŝliwość uŝycia zmiennych globalnych niebezpieczeństwa z tym związane wyjście wywołanie funkcji z adresami zmiennych Przykład: // adresy (wskaźniki) jako argumenty funkcji #include <iostream> using namespace std; const double PI=3.1415926; void kula(double r, double *pow, double *obj); main() double r,s,v,*p; p=&v; r=1.0; kula(r, &s, p); cout << "Pole powierzchni = " << s << endl; cout << "Objetosc = " << *p << endl; return 0; void kula(double r, double *pow, double *obj) *pow=4*pi*r*r; *obj=4*pi*r*r*r/3; Pole powierzchni = 12.5664 Objetosc = 4.18879 bardzo częste stosowane gdy przekazujemy tabelę, to przekazujemy adres jej początku
Jak to działa? int a, *wa; cout << "adres a : " << &a << " wartosc a : " << a << endl; cout << "adres wa : " << &wa << " wartosc wa : " << wa <<endl;wa=&a; *wa=1; adres a : 0x22ff74 wartosc a : 2 adres wa : 0x22ff70 wartosc wa : 0x7 1. Definicja zmiennych: int a, *wa; a 0x74 wa 0x70 2. Podstawienie: wa=&a; a 0x74 wa 0x74 0x70 3. Podstawienie: *wa=1; a 1 0x74 wa 0x74 0x70 Poprzez odwołanie pośrednie moŝemy za pomocą wskaźnika zmieniać wartość innych zmiennych (tych, na które pokazuje wskaźnik). To jest kluczowe przy wykorzystaniu wskaźników jako argumentów funkcji. W naszym przykładzie:
1. Definicja zmiennych: double r, s, v, *p; r s v p 0x70 0x68 0x60 0x5c 2. Podstawienie: p=&v; r s v 0x70 0x68 0x60 p 0x60 0x5c 3. Podstawienie: r=1.0; r 1.000000 0x70 s v 0x68 0x60 p 0x60 0x5c 4.Wywołanie funkcji: kula(r,&s,p); kula(r,*pow,*obj); r 1.000000 0x70 r 1.000000 0x30 s 0x68 pow 0x68 0x38 v 0x60 obj 0x60 0x3c p 0x60 0x5c
4.Wyliczenia w funkcji: *pow=4*pi*rr*rr; *obj=4*pi*rr*rr*rr/3; r 1.000000 0x70 r 1.000000 0x30 s 12.566 0x68 pow 0x68 0x38 v 4.1888 0x60 obj 0x60 0x3c p 0x60 0x5c 5. Zakończenie funkcji kula zmienne r, pow, obj są kasowane, ale zawartość komórek pamięci na króre wskazywały pow, obj (czyli komórek s oraz v zmiennych lokalnych main) pozostaje!!! 6.Wydruk zawartości komórek s, v drukuje wyliczone w funkcji kula wartości kluczowy punkt mając adres jakiegoś obiektu (wskaźnik) moŝemy nim manipulować mimo tego, Ŝe punkt manipulacji jest (formalnie rzecz biorąc) poza zakresem waŝności tego obiektu ta metoda pozwala obliczyć i wycofać z funkcji obliczone wartości dowolnej ilości zmiennych poprawny sposób komunikacji pomiędzy funkcjami w C/C++ - patrząc tylko na miejsce wywołania funkcji moŝemy z góry powiedzieć, które argumenty mogą być zmieniane przez operacje wewnątrz funkcji tak, by zmiany te były aktualne po zakończeniu działania funkcji to umoŝliwia/ułatwia analizę programu.
Zmienne referencyjne w C++ moŝna tworzyć zmienne referencyjne do istniejących juŝ zmiennych zmienne takie to aliasy (przezwiska) innych zmiennych zarówno zmienna jak i alias odpowiadają temu samemu miejscu w pamięci int a=5; // definicja i inicjalizacja a int &b=a; // b jest referencją do a, wartosc b=5 int &c; // nielegalna definicja, referencja musi być // zainicjowana b=10; // a=10, b=10 a=20; // a=20, b=20 w C++ referencje uŝywane najczęściej w funkcjach, które zmieniają swoje argumenty (alternatywa dla wskaźników) #include <iostream> using namespace std; void podwoj(int a, int &b) a *= 2; b *= 2; int main() int a=1, b=2; podwoj(a,b); cout << "a=" << a << " b=" << b << endl; a=1 b=4 argument przesłany przez wartość bez zmian; przesłany przez referencję zmodyfikowany powód przez referencję przesyłamy adres argumentu (a nie jego wartość)
o tym, Ŝe jest to wywołanie argumentu przez referencję decyduje kształt definicji funkcji; samo wywołanie wygląda identycznie jak dla przypadku przekazania argumentu przez wartość utrudnienie analizy kodu; niebezpieczeństwo nieumyślnej zmiany argumentu główne zastosowanie przesłanie do funkcji danych duŝych rozmiarów (struktur, klas) Przykład test #include <cstdlib> #include <iostream> using namespace std; double gg(double a, double *c) *c = *c + a; a=15; *c = *c - a; return a-3; int main() double a,b,c; a=2; c=3; b=gg(a,&c); cout << a << " " << b << " " << c << "\n"; system("pause"); return 0; co wypisze powyŝszy program? jak zmieni się odpowiedź, gdy pierwszy argument gg będzie referencją?
Domyślne argumenty funkcji w większości języków funkcje muszą być wywołane z taką ilością argumentów, dla jakiej zostały zdefiniowane C++ dostarcza mechanizmu zmiany tej zasady poprzez domyślne argumenty funkcji argument domyślny taki, który został zainicjowany w deklaracji funkcji o tym, Ŝe argument jest domyślny informujemy kompilator tylko raz (w deklaracji funkcji) Przykład: void ff(int a, int b=2, int c=4); main() ff(1); ff(1,1); ff(1,1,1); // ff(1,2,4) // ff(1,1,4) // ff(1,1,1) void ff(int a, int b, int c) //deklaracja funkcji //definicja funkcji Uwaga: nie da się opuścić argumentu b a podać c: zapis ff(1,,3) błędny!!! ; w C++ nie moŝe wystąpić sytuacja, w której dwa przecinki są obok siebie w trakcie kompilacji kompilator moŝe (w danym zakresie waŝności) napotkać definicję argumentu domniemanego tylko jednokrotnie void f1(int a, int b=2); void f1(int, int=2); // błąd, powtórzenie deklaracji
void f2(int, int=3); // poprawne, wartości void f2(int=2, int); // domniemane: 3,2 void f3(int a=3, int b); // czy to jest void f3(int a, int b=2); // poprawne? Typowe zastosowania: gdy mamy sytuację, w której wartość jednego argumentu często się powtarza. Przykład: funkcja drukująca temperaturę w jednej z trzech skal (Celsjusza, Kelvina i Fahrenheita); najczęściej w skali Celsjusza void temperatura(float stopnie, int skala=0) cout << Temperatura wynosi: cout.setf(ios::fixed); cout.precision(1); // ustala notację liczb rzeczywistych // ustala dokładność switch (skala) case 0: cout << 1.0*stopnie << endl; break; case 1: cout <<stopnie+273.2<<endl; break; case 2: cout << (9*stopnie/5+32.0)<<endl; break; Wywołanie dla skali Celsjusza: //Celsjusz //Kelvin //Fahrenheit temperatura(27.3); //temp(27.3, 0)
Funkcje inline dla krótkich funkcji (o krótkim ciele) efektywniej jest wpisać ciało funkcji w miejsce jej wywołania zamiast ją wywoływać (powód koszt wywołania funkcji, zwłaszcza dla wielokrotnych wywołań) Przykład: int zao(float liczba) return (liczba + 0.5); lepiej zamiast: k=zao(m) + zao(n*13.3); wpisać: k=(int)(m+0.5) + (int)((n*13.3)+0.5); jak to zrobić, by się duŝo nie napracować? o klasyczne C poprzez makrodefinicje: #define zao(a) ((int)((a)+0.5)) o w C++ dodatkowa moŝliwość deklaracja typu funkcji inline inline int zao(float liczba) return (liczba + 0.5); o inline (słowo kluczowe C++) wskazanie dla kompilatora, by dokonywać rozwinięcia (wstawienia) ciała funkcji w miejsce wywołania o istnieje moŝliwość, Ŝe kompilator zignoruje tą deklarację i skompiluje funkcję normalnie (na pewno tak będzie, gdy kompilacja do pracy z debuggerem)
o aby kompilator mógł wstawić ciało funkcji musi mieć całą definicję funkcji (a nie tylko deklarację) przed pierwszym uŝyciem funkcji o funkcje typu inline umieszczamy na samej górze tekstu programu (często w pliku nagłówkowym) o mechanizm inline pomyślany tylko dla małych funkcji (i tylko dla takich powinien być stosowany)