w05


Funkcje i struktura programu
" Po co stosować funkcje
o mo\liwość podziału du\ego przedsięwzięcia
obliczeniowego na mniejsze zadania (łatwiejsze
do napisania, analizy i testowania)
o mo\liwość ukrycia nieistotnych szczegółów
(wa\ne  co a nie  jak )  większa przejrzystość
całości, łatwość wprowadzania zmian
o funkcje napisane raz mo\emy zastosować do
innych celów (powtórne wykorzystanie kodu);
funkcje biblioteczne
" Dobra funkcja  realizuje jedno, dobrze
określone zadanie
" Ogólna struktura funkcji
typ_zwracany nazwa(lista_argumentów)
{
deklaracje
instrukcje
}
" gdy nie podano typu zwracanego  domyślnie int
" nawiasy (...) konieczne
" gdy brak argumentów  (void)
" zwracanie wartości: return wyra\enie;
" funkcji nie mo\na zagnie\d\ać  są względem
siebie zewnętrzne
Przykład:
#include
#include // udistępnia manipulatory
using namespace std;
// deklaracja i definicji funkcji
// wywołanie funkcji
int potega(int m, int n); // deklaracja funkcji potega
int main()
{
int i;
for (i=0; i<5; i++)
cout << setw(3) << i << setw(5) << potega(2,i)
<< setw(5) << potega(-3,i) << endl;
return 0;
}
// definicja funkcji potega:
// podnies baza do potegi wykl; wykl >=0
int potega(int baza, int wykl)
{
int p=1;
for (int i=1; i<=wykl; i++)
p*= baza;
return p;
}
0 1 1
1 2 -3
2 4 9
3 8 -27
4 16 81
Definicje i wywołania funkcji
" definicje funkcji mogą być umieszczone w dowolnej
kolejności (w jednym lub kilku plikach zródłowych);
definicja funkcji nie mo\e być rozdzielona pomiędzy
plikami
" funkcja nie mo\e być u\yta przed zdefiniowaniem
lub zadeklarowaniem  w przykładzie powy\ej
konieczna deklaracja
" rozró\nienie pomiędzy definicją a deklaracją
o definicja  dotyczy miejsca, w którym obiekt jest
faktycznie tworzony (przypisuje się jej miejsce w
pamięci)
o deklaracja  dotyczy miejsca, w której określa się
naturę obiektu ale nie przydziela mu pamięci
o nie musi podawać nazw parametrów  wystarczą typy
int potega(int, int);
" wywołanie funkcji  napisanie jej nazwy wraz z
ujętymi w nawiasy argumentami
" je\eli typ funkcji ró\ny od void to ka\da ście\ka
wykonania funkcji powinna kończyć się instrukcją
return wyra\enie;
" je\eli funkcja typu void  powrót przez return lub
osiągnięcie końca bloku funkcji
" rozró\nienie pomiędzy argumentem formalnym (w
definicji funkcji) a argumentem aktualnym (w
wywołaniu funkcji)
Przekazywanie argumentów do funkcji
" w C przy wywołaniu funkcji wszystkie argumenty
aktualne są przekazywane funkcji  przez wartość
" oznacza to, \e funkcja nie ma dostępu do
oryginalnego obiektu argumentu aktualnego, a tylko
do jego kopii, przekazywanej poprzez zmienną
tymczasową, będącą lokalną zmienną funkcji
" funkcja mo\e u\ywać tych lokalnych kopii do
obliczeń, mo\e je modyfikować, ale te zmiany nie
przenoszą się na oryginalną wartość argumentu
aktualnego
Przykład:
#include
using namespace std;
void zwieksz(int a)
{
a++;
}
main()
{
int b=3;
zwieksz(b);
cout << "b jest rowne " << b << endl;
}
" zwiększenie kopii argumentu aktualnego nie
przenosi się na oryginał
Wa\ny wniosek: na podstawie tego co wiemy do tej
pory funkcja mo\e zmodyfikować i zwrócić (poprzez
return) do funkcji wywołującej wartość nie więcej ni\
jednego wyra\enia.
Przykład:
include
using namespace std;
#define PI 3.1415926
double kula(double);
main()
{
double r,v,s;
r=1.0;
s=kula(r);
cout.precision(3);
cout << "Pole powierzchni = " << s << endl;
//cout << "Objetosc = " << v << endl; // obliczone ale nieprzekazane
return 0;
}
double kula(double r)
{
double s,v;
s=4*PI*r*r;
v=4*PI*r*r*r/3;
return s;
}
" W funkcji mo\emy obliczyć wiele wielkości, ale zwrócić
mo\emy tylko jedną
Zmienne lokalne
" zakres wa\ności: zmienne zdefiniowane w pewnym
bloku  dostępne tylko w nim (zmienne lokalne)
" czas \ycia: zmienna lokalna zaczyna istnieć w
momencie rozpoczęcia wykonania bloku, w którym
została zdefiniowana i znika w chwili wyjścia z tego
bloku (pojawia się i znika automatycznie  stąd
nazwa: zmienne automatyczna).
" przechowywane na stosie
" wartości zmiennych automatycznych w momencie
ich tworzenia są nieustalone (zawierają śmieci)
" gdy obiekt typu auto przestanie istnieć, to nie ma
powodu, by po powtórnym jego utworzeniu
zachował wartość jaką miał w chwili unicestwienia -
zmiennych takich nie mo\na u\ywać do
przechowania informacji potrzebnych w następnym
wywołaniu bloku
" jest całkiem mo\liwe, \e przy powtórnym utworzeniu
obiekt został zdefiniowany w innym obszarze
pamięci (czyli z innym adresem) ni\ przy
wcześniejszym wywołaniu
Zmienne zewnętrzne (globalne)
" obiekty zewnętrzne  zdefiniowane poza obszarem
jakiegokolwiek bloku funkcji; są to wszystkie funkcje
oraz ewentualnie zmienne
" obiekty takie są globalne
o zakres wa\ności ich nazwy to obszar od miejsca
gdzie zostały zdefiniowane do końca pliku
o czas \ycia: zmienne globalne są tworzone w chwili
rozpoczęcia działania programu i kasowane w chwili
zakończenia jego działania  a więc zachowują swe
wartości pomiędzy wywołaniami funkcji
o zmienna globalna  przechowywana w  normalnym
obszarze pamięci; jest zerowana w chwili utworzenia
o definicja zmiennej globalnej  tylko raz; deklaracja
poprzez extern
" zmienne globalne mogą być u\yte do komunikacji
pomiędzy funkcjami
#include
using namespace std;
#define PI 3.1415926
double s,v; // zmienne globalne
void kula(double);
int main()
{
double r;
r=1.0;
kula(r);
cout.setf(ios::fixed); // ustala notację liczb rzeczywistych
cout.precision(3); // ustala dokładność
cout << "Pole powierzchni = " << s << endl;
cout << "Objetosc = " << v << endl;
return 0;
}
void kula(double r)
{
s=4*PI*r*r;
v=4*PI*r*r*r/3;
}
" przekazywanie argumentów przez zmienne globalne 
przydatne w sytuacji gdy funkcja wymaga dostępu do
du\ej ilości argumentów (zamiast długiej listy
argumentów  zmienne zewnętrzne)
" wada  powstają programy, w których powiązania
pomiędzy danymi (poprzez zmienne zewnętrzne) nie
są oczywiste, obiekty mogą być zmieniane w sposób
nieoczekiwany (a nawet nieumyślny)
Zmienne statyczne
1. Zmienne lokalne
" dla normalnej zmiennej lokalnej (auto) czas \ycia
ograniczony do czasu wykonania bloku, w której
się pojawia
" mo\na temu zapobiec deklarując zmienną jako
statyczną
static int licznik;
" takie zmienne
o są przechowywane w tej samej części
pamięci, co zmienne globalne
o są wstępnie zerowane
o przechowują wartość równie\ pomiędzy
kolejnymi wywołaniami bloku (stanowią
prywatną, stałą pamięć wewnątrz danego
bloku)
Przykład:
#include
using namespace std;
int szlak(int dlug);
int main()
{
int m,k;
for (k=5;k<11;k++)
if (k%2) m=szlak(30);
cout << "Liczba szlaczkow : " << m << endl;
return 0;
}
int szlak(int dlug)
{
static int lk;
for ( ;dlug>0; dlug--) cout << "*";
cout << "\n";
return ++lk;
}
2. Zmienne globalne i funkcje
" wyposa\enie nazwy funkcji lub zmiennej
zewnętrznej w przydomek static oznacza, \e
jest on dostępny tylko w danym pliku (jest
niewidoczna dla innych plików programu). Jest to
metoda ukrycia tych nazw (stosowana często
przy pisaniu bibliotek)
" w C++ - lepsze rozwiązanie  bezimienna
(anonimowa) przestrzeń nazw
o definiujemy w pliku bezimienną przestrzeń nazw
o umieszczamy w niej definicje/deklaracje nazw
zmiennych globalnych/funkcji, które mają być
niewidoczne poza swoim plikiem
o nazwy umieszczone w takiej przestrzeni są
automatycznie widoczne w swoim pliku
o bezimienna przestrzeń nazw jest unikalna w
ka\dym pliku  jednostce translacji, czyli:
o nie ma sposobu, by uzyskać dostęp do nazw
umieszczonych w anonimowej przestrzeni nazw
pliku A.cpp z ka\dego innego pliku
Przykład: chcemy zastrzec dostęp (spoza tego pliku, w którym są zdefi-
niowane) do zmiennych globalnych: moja, x i funkcji: wazna, podstawy:
1. Stare rozwiązanie (C):
// początek pliku bibliotecznego
& .
static int moja;
static double x;
static int podstawy(int, double)
{
& .
}
static void wazna(int,int);
& .
// koniec pliku bibliotecznego
2. Nowe rozwiązanie (C++)
// początek pliku bibliotecznego
& .
namespace // bez nazwy, czyli anonimowa
{
int moja;
double x;
int podstawy(int, double)
{
& .
}
void wazna(int,int);
}
& .
// koniec pliku bibliotecznego
Program składający się z kilku plików
Zasady ogólne:
" Podział na pliki nie mo\e rozdzielać jednej funkcji
" Aby zmienne globalne i funkcje zdefiniowane w jednym
pliku mogły być u\ywane w innych, muszą w nich być
dodatkowo zadeklarowane; dla zmiennych globalnych
taka deklaracja musi zawierać słowo kluczowe
extern, dla funkcji to po prostu prototyp funkcji
" Często wszystkie takie deklaracje umieszczamy w
dodatkowym, osobnym pliku nagłówkowym (który
wstawiamy w treść poszczególnych plików dyrektywą
#include)
Przykład: program z dwóch plików + plik nagłówkowy
Plik nagl.h
extern int ile_bialych;
extern int ile_czarnych;
void piony_1();
void piony_2();
void piony_3();
void piony_4();
Plik plik1.cpp
#include
#include "nagl.h"
using namespace std;
int ile_bialych=5;
void piony_1()
{
cout << "Jestem w funkcji pierwszej:\n";
cout << "Mamy jeszcze " << ile_bialych << " pionow bialych i "
<< ile_czarnych << " pionow czarnych\n";
ile_bialych--;
ile_czarnych++;
piony_3();
}
void piony_2()
{
cout << "Jestem w funkcji drugiej:\n";
cout << "Mamy jeszcze " << ile_bialych << " pionow bialych i "
<< ile_czarnych << " pionow czarnych\n";
ile_czarnych+=2;
piony_4();
}
Plik plik2.cpp
#include
#include "nagl.h"
using namespace std;
int ile_czarnych=7;
int main()
{
cout << "Poczatek programu\n";
ile_bialych+=5;
piony_1();
piony_2();
cout << "Pozostalo " << ile_bialych << " pionow bialych i "
<< ile_czarnych << " pionow czarnych\n";
cout << "Koniec programu\n";
}
void piony_3()
{
ile_bialych++;
cout << "Jestem w funkcji trzeciej:\n";
cout << "Mamy jeszcze " << ile_bialych << " pionow bialych i "
<< ile_czarnych << " pionow czarnych\n";
piony_4();
}
void piony_4()
{
ile_czarnych--;
cout << "Jestem w funkcji czwartej:\n";
cout << "Mamy jeszcze " << ile_bialych << " pionow bialych i "
<< ile_czarnych << " pionow czarnych\n";
ile_bialych*=2;
}
Poczatek programu
Jestem w funkcji pierwszej:
Mamy jeszcze 10 pionow bialych i 7 pionow czarnych
Jestem w funkcji trzeciej:
Mamy jeszcze 10 pionow bialych i 8 pionow czarnych
Jestem w funkcji czwartej:
Mamy jeszcze 10 pionow bialych i 7 pionow czarnych
Jestem w funkcji drugiej:
Mamy jeszcze 20 pionow bialych i 7 pionow czarnych
Jestem w funkcji czwartej:
Mamy jeszcze 20 pionow bialych i 8 pionow czarnych
Pozostalo 40 pionow bialych i 8 pionow czarnych
Koniec programu
Rekurencja
" w C/C++ dopuszczalne jest wywołanie rekurencyjne
(funkcja mo\e wywołać siebie bezpośrednio lub
pośrednio)
" zasady obowiązujące przy rekursywnym wywołaniu
funkcji:
o ka\de wywołanie funkcji  utworzenie kopii
argumentów aktualnych, wykonaniu operacji na
kopiach. Zmiany na kopiach nie wpływają na
oryginały argumentów aktualnych
o ka\de wywołanie funkcji  utworzenie kompletu
wszystkich zmiennych automatycznych
" rekurencja nie przyśpiesza działania programu ani
nie oszczędza pamięci
" rekurencja sprawia, \e programy są bardziej zwarte,
czasem łatwiejsze do napisania
Przykład: silnia
#include
using namespace std;
// przykład rekurencji
int silnia(int k);
int main()
{
int w=silnia(6);
cout << "6! = " << w << endl;
return 0;
}
int silnia(int k)
{
cout << "Funkcja silnia - wywolana z parametrem " << k << endl;
if (!k) return 1;
return (k*silnia(k-1));
}
Funkcja silnia - wywolana z parametrem 6
Funkcja silnia - wywolana z parametrem 5
Funkcja silnia - wywolana z parametrem 4
Funkcja silnia - wywolana z parametrem 3
Funkcja silnia - wywolana z parametrem 2
Funkcja silnia - wywolana z parametrem 1
Funkcja silnia - wywolana z parametrem 0
6! = 720
Przykład: wie\e Hanoi
Mamy trzy wie\e A, B, C. Na pierwszej z nich jest
nało\onych (im mniejszy promień tym wy\ej) N krą\ków.
Zadanie: przenieść krą\ki z A na B (u\ywając
ewentualnie C).
Ograniczenia:
" wolno przenosić tylko jeden krą\ek na raz;
" nie wolno umieszczać krą\ka mniejszego na
większym.
Naturalne rozwiązanie rekurencyjne:
Procedura: przenieś(N, z X na Y u\ywając Z)
Je\eli N=1 to wypisz X Y
w przeciwnym razie:
wywołaj przenieś(N-1, z X na Z u\ywając Y)
wypisz X Y
wywołaj przenieś(N-1, z Z na Y u\ywając X)
zakończ
" pokazuje siłę rekurencji jako metody uzyskiwania
prostych rozwiązań zło\onych problemów
" uwaga  proste rozwiązanie nie zawsze znaczy szybkie
w działaniu; często rozwiązania rekreacyjne działają
dłu\ej ni\ iteracyjne
" by rekurencja nie trwała w nieskończoność musi być
warunek zatrzymujący rekurencję (i związany z nim
alternatywny sposób zakończenia funkcji)
" rekurencja jako instrukcja sterująca  w naturalny
sposób połączona z drzewem jako strukturą danych
(podobnie jak tablice i pętle)
#include
#include
using namespace std;
void przenies(int n,char p,char d,char t)
{
if (n==1)
cout << p <<" --> " << d << endl;
else
{
przenies(n-1,p,t,d);
cout << p <<" --> " << d << endl;
przenies(n-1,t,d,p);
}
}
int main()
{
int i;
cout << "Podaj ilosc krazkow: ";
cin >> i;
przenies(i,'A','B','C');
system("PAUSE");
return EXIT_SUCCESS;
}


Wyszukiwarka

Podobne podstrony:
W05 Fizyka Haran
w05 wypełnianie obszaru
2013 w05 1 INT uzu dla?515 13z
W05 Czujniki inteligetne
w05 info
W05 Log prod1 system
PMK W05 cholestaza zewnatrzwatrobowa V2
SR W05 JavaRMI
2013 w05 DMA HWI 2013zid(362
W05 prognozowanie
472 W05 SKiTI modelTCPIP warstwa 3
Gazownictwo w05
E Pawlowski wyklad ME EINS 2012 w05 06
0708z sieciTM w05

więcej podobnych podstron