kot-niepłot
Dołączył: 29 Lis 2005
Posty: 324
Przeczytał: 0 tematów
Ostrzeżeń: 0/5 Skąd: Gliwice BioAut
|
Wysłany: Śro 1:32, 20 Gru 2006 Temat postu: Pseudokurs: funkcje |
|
|
Funkcje nie są niezbędne, ale porządkują kod i ułatwiają pracę - najczęściej wtedy, gdy ten sam - lub bardzo podobny kod piszemy kilka razy - to najlepszy materiał na funkcję. Funkcjami są np. main(), printf(), rand(), sqrt i wiele innych.
Definicja funkcji:
TYP_ZWRACANY nazwa_funkcj(LISTA_PARAMETRÓW)
{
/* Ciało funkcji */ return WARTOŚĆ;
}
Po kolei:
TYP_ZWRACANY - dowolny znany nam typ, czyli np. int, double. Można zwracać także wskaźnik: int* (ale ostrożnie z tym, mogę smakoszom wyjaśnić czemu). Dodatkowym możliwym typem jest void - czyli po prostu nie zwracanie niczego - nie można takiej funkcji użyć w wyrażeniach (o czym potem) (Odpowienikiem pojęcia funkcji zwracającej void w innych językach jest procedura). Wtedy nie piszemy słowa return.
WARTOŚĆ - jest to wartość, jaką zwróci funkcja, musi być tego samego typu co TYP_ZWRACANY. Pojęcie "zwracania" opisane niżej.
LISTA_PARAMETRÓW - dowolna liczba zmiennych (oddzielone przecinkiem), które są deklarowane już w tym miejscu i nadawana jest im wartość początkowa taka, jak wartości przesyłane do funkcji w momencie wywołania (o czym poniżej). Są normalnymi zmiennymi lokalnymi.
Przykład:
double podziel(int x, int y)
{ return 1.0*x/y; }
Prototyp funkcji:
Jeśli chcemy użyć funkcji wcześniej (wyżej w kodzie) niż jest zdefiniowana, musimy przed funkcją w której jej używamy napisać prototyp. W liście_parametrów nie trzeba pisać nazw zmiennych
TYP nazwa(lista_parametrów);
Np:
Kod: |
double podziel(int, int);
int main()
{ printf("%g", podziel(5,3));
return 0; }
double podziel(int x, int y)
{ return 1.0*x/y; } |
Pojawił się nowy element: używanie funkcji:
funkcję zwracającą inny typ niż void możemy normalnie stosować w wyrażeniach, w nawiasach podając parametry (liczby, zmienne lub nawet całe wyrażenia) oddzielone przecinkami.
Inny przykład:
x = (-b+sqrt(delta))/2;
y = sqrt(podziel(2,3));
z = podziel(3*x, 2*sqrt(2));
Zwracanie wartości:
Jest to wartość którą wpisujemy po return. Ona się pojawi w miejscu wywołania funkcji.
Przykład:
x = podziel(2, 3); zadziała tak samo jak x = 1.0*2/3;
x = podziel(y, 2); zadziała jak x = 1.0*y/2; y - to już tylko wartość zmiennej y, nie sama zmienna jako taka (jest różnica!), o tym później.
Zwracanie void:
Użycie jej w wyrażeniu: x = zwraca_void() da błąd w kompilacji.
Używa się ich jedynie jako osobna linijka:
zwraca_void();
(Jeśli nie ma parametrów, nawiasy też trzeba pisać, tylko puste)
Jako parametr wywoływanej funkcji można wpisać całe wyrażenie. Jest ono obliczane przed wywołaniem samej funkcji, a do funkcji przesyłana jego wartość, czyli:
podziel(x+3, 2); dostanie wartość x powiększone o 3
podziel(++x, 2); najpierw zwiększy x o jeden, funkcja dostanie "nową" wartość x;
Inne wyjaśnienie idei funkcji:
Funkcja działa zupełnie tak samo jak w matematyce.
Np. mamy zdefiniowaną funkcję f(x) = x+1. W nawiasach mamy podaną listę parametrów (są przecież funkcje z wieloma parametrami: f(a,b) = a*b ). Różnica jest taka, że mamy większy wybór jeśli chodzi o nazwę funkcji oraz musimy określić typy parametrów (matematyka nie rozróżnia typów ). Wartość zwracana to parametr x plus 1. Funkcje w matematyce możemy również stosować w wyrażeniach ( y=f(x) ). Jedyne co, to że funkcje w programowaniu mogą być o wiele bardziej skomplikowane i wykonywać wiele różnych zadań a zwracać w końcu jedną wartość.
PRZEKAZYWANIE PARAMETRóW:
Zmora
Przez wartość:
Podczas wywołania przekazywana jest WARTOŚĆ zmiennej/wyrażenia. Wewnątrz funkcji zmienne będące parametrami są zwykłymi zmiennymi, które możemy zmieniać i wyprawiać cuda. Nadawana jest im wartość początkowa taka, jak ta wartość przekazywana do funkcji.
Z przykładu (np. dla a równego 4):
podziel(a+2, 3); - wewnątrz funkcji zmienna x przyjmie wartość 6, a y wartość 3.
Przez wzkaźnik:
Definicja funkcji wygląda tak: (zmienne deklaruje się jako 'normalne' wskaźniki)
double podziel_wsk(int *a, int *b) { ... }
Prototyp tak:
double podziel_wsk(int*, int*); // nie trzeba podawać nazw w prototypie
Zmiennych używamy jako normalnych wskaźników, ale wskazują one na zmienną, z którą się wywołuje funkcję:
podziel_wsk(&x, &y); // należy podawać adresy do zmiennych!
Zaletą tego jest to, że jest szybkie (można przekazać nawet całą tablicę) ale przede wszystkim to, że można wewnątrz funkcji zmienić przekazywaną zmienną. Robi się to, jak z każdym innym wskaźnikiem (najpierw trzeba je rozumieć i umieć ).
Typ zmiennej musi się zgadzać z typem, jaki funkcja przyjmuje.
Nie pamiętam który to jest formalny, a który aktualny - dopiszcie to
GLOBALNOŚĆ I LOKALNOŚĆ (w skrócie ZASIĘG) ZMIENNYCH, zasłanianie zmiennych:
Zmienne deklarowane wewnątrz funkcji obowiązują tylko i wyłącznie w jej obrębie - są to zmienne lokalne. Są niszczone po wyjściu z funkcji - ich wartości się tracą, a pamięć zwalniana.
Zmienne deklarowane poza funkcjami są globalne. Są niszczone dopiero w czasie kończenia programu. Można je zmieniać w każdej funkcji jako normalne zmienne.
Jeśli wewnątrz funkcji zadeklarujemy zmienną o tej samej nazwie jak jakaś globalna - w ramach tej funkcji będziemy posługiwać się zmienną lokalną - lokalna jest "ważniejsza" i "zasłania" globalną. Jeśli jednak chcemy się wtedy odwołać do globalnej piszemy np. :: x //bez spacji, czyli:
Kod: |
int x;
void funkcja()
{ double x, y;
y = sqrt(x); // odwołanie do lokalnego x
x = ::x + 2; // lokalne x = globalne x + 2
::x = x+2; // globalne x = lokalne x + 2
} |
Jeśli chodzi o zasięg zmiennych jest jeszcze jeden aspekt, TYLKO w C++ (bo w końcu nie wiadomo czego nas uczą): nawiasy klamrowe otwierają nowy zasięg, czyli normalnie mamy zasięg globalny, wewnątrz funkcji bardziej lokalny - wewnątrz nawiasów klamrowych. Można zadeklarować też zmienną wewnątrz np. if i ona będzie jeszcze bardziej lokalna i obowiązuje jedynie do zamknięcia nawiasu klamrowego. Obowiązują wtedy też normalne reguły zasłaniania:
Kod: |
int y; // jakaś zmienna lokalna
for(int x=0; x<3; x++) // deklarowana wewnątrz pętli obowiązuje tylko wewnątrz pętli
{ tab[x] = x*2;
if(tab[x]==0)
{ int y; // zmienna jeszcze bardziej lokalna
y = x/2; // NIE WIEM jak w tym przypadku działa operator zasięgu: ::y
tab[x]++;
} // zmienna z if przestaje istnieć, teraz obowiązuje ta "pierwsza" y
} |
Podsumowanie:
Wesołych świąt
Post został pochwalony 0 razy
|
|