Informatyka 1 - Pracownia
Strona 1 z 9
© 2011 Jarosław Forenc
MATERIAŁY POMOCNICZE NR 7
DO PRACOWNII Z PRZEMIOTU „INFORMATYKA 1”
Funkcje
W programie w języku C zawsze występuje co najmniej jedna funkcja - main().
Od funkcji main() rozpoczyna się wykonanie całego programu.
Oprócz niej mogą występować także inne funkcje zdefiniowane przez użytkownika.
Ogólna struktura funkcji w języku C jest następująca:
float add(float a, float b)
{
float y;
y = a + b;
return y;
}
argumenty
przekazywane do funkcji
nazwa funkcji
typ wartości
zwracanej
przez funkcję
instrukcja
nagłówek funkcji
ciało funkcji
wartość zwracana
przez funkcję
instrukcja
instrukcja
Przykład nr 8.1
Funkcja dodająca dwie liczby.
#include <stdio.h>
#include <conio.h>
float add(float a, float b)
{
float y;
y = a + b;
return y;
}
int main()
{
float x1=10.0, x2=20.0, wynik;
wynik = add(x1,x2);
printf("Wynik = %f\n", wynik);
getch()
return 0;
}
Wykonanie powyższego programu rozpoczyna się od funkcji main().
Informatyka 1 - Pracownia
Strona 2 z 9
© 2011 Jarosław Forenc
Gdy dochodzimy do instrukcji zawierającej funkcję add(), to wywołanie tej funkcji
powoduje przekazanie sterowania do jej pierwszej instrukcji.
Do funkcji add() przekazywane są dwa parametry typu float. Pierwszy parametr (a)
otrzymuje wartość pierwszego parametru wywołania funkcji (x1), natomiast drugi
parametr (b) - wartość drugiego parametru wywołania funkcji (x2).
Powrót z funkcji (do miejsca zaraz po jej wywołaniu) następuje na skutek wykonania
instrukcji return.
Wartość zwracana przez funkcję podstawiana jest pod zmienną wynik.
Po słowie return może występować dowolne wyrażenie. Wyrażenie to często
umieszczane jest w nawiasach, ale nie jest to konieczne.
Funkcję add() można zapisać w prostszy sposób:
float add(float a, float b)
{
return (a+b);
}
W wywołaniu funkcji jako parametry mogą występować zmienne, wyrażenia
arytmetyczne lub stałe liczbowe, np.
wynik = add(10,20);
wynik = add(x1*20+4,x1/x2);
Istnieją funkcje, które nie zwracają żadnej wartości, wtedy jako typ zwracanego wyniku
podajemy void, np.
void drukuj(int a)
{
printf(“Liczba a wynosi: %d \n”,a);
return;
}
Wywołanie takiej funkcji w programie ma postać:
drukuj(-10);
Powyższa funkcja nie zwraca żadnego wyniku więc wystarczy samo słowo return. Jeśli
funkcja nie zwraca wyniku i nie ma w jej ciele return to sterowanie wraca do punktu
wywołania na skutek zakończenia wykonania wszystkich instrukcji w bloku funkcyjnym.
Mogą istnieć funkcje, do których nie przekazujemy żadnych parametrów formalnych, np.
void linia()
{
printf(”----------\n”);
lub
}
void linia(void)
{
printf(”----------\n”);
}
Wywołanie takich funkcji w programie ma postać:
linia();
- nawiasy są konieczne.
Ale już zapis:
linia(void)
{
printf(”----------\n”);
}
Informatyka 1 - Pracownia
Strona 3 z 9
© 2011 Jarosław Forenc
nie jest równoważny powyższym zapisom, gdyż jeśli nie podamy typu zwracanej
wartości przez funkcję, to domyślnie jest to typ int.
Umieszczanie definicji funkcji w programie
Definicje funkcji można umieszczać w dowolnym miejscu programu - przed lub po
funkcji main(). Należy jednak pamiętać o tym, że zasięg widzialności funkcji rozpoczyna
się od miejsca jej deklaracji.
Jeśli chcemy umieścić definicję funkcji po funkcji main(), czyli po miejscu, w którym
jest wywoływana, to musimy wcześniej podać jej formalną deklarację czyli prototyp.
Prototyp opisuje to samo co nagłówek, ale kończy się średnikiem. W prototypie nie
musimy podawać nazw argumentów formalnych - wystarczą tylko typy.
Podanie nazw argumentów ma jednak znaczenie dla czytelności programu.
Dzięki prototypom kompilator ma możliwość sprawdzenia zgodności typów formalnych
i faktycznych.
Rozpatrzmy strukturę następujących dwóch programów:
#include …
void fun(int a, int b);
int main()
{
…
fun(a,b);
…
}
void fun (int a, int b)
{
…
}
#include …
void fun (int a, int b)
{
…
}
int main()
{
…
fun(a,b);
…
}
W programie po lewej stronie funkcja fun() zdefiniowana jest po funkcji main(), dlatego
przed funkcją main() umieszczony jest prototyp funkcji fun(), który może mieć jedną
z dwu postaci:
void fun(int a, int b);
lub
void fun(int, int);
W programie po prawej stronie umieszczenie prototypu funkcji fun() nie jest konieczne,
gdyż definicja funkcji fun() umieszczona jest przed funkcją main().
Przekazywanie parametrów do funkcji przez wartość
Przekazywanie parametrów przez wartość oznacza, że po przekazaniu sterowania do
funkcji tworzone są kopie zmiennych przekazywanych do funkcji i wszystkie działania
w funkcji wykonywane są na kopiach.
Informatyka 1 - Pracownia
Strona 4 z 9
© 2011 Jarosław Forenc
Przykład nr 8.2
Przekazywanie parametrów do funkcji przez wartość.
#include <stdio.h>
#include <conio.h>
void fun(
int a
,
int b
)
{
printf("fun1: a=%3d, b=%3d \n",
a
,
b
);
a
= 10;
b
= 10;
printf("fun2: a=%3d, b=%3d \n",
a
,
b
);
}
int main()
{
int
a
= 20,
b
= 20;
printf("main1: a = %3d, b = %3d \n",
a
,
b
);
fun(
a
,
b
);
printf("main2: a = %3d, b = %3d \n",
a
,
b
);
getch();
return 0;
}
Wynik działania programu:
main1: a = 20, b = 20
fun1: a = 20, b = 20
fun2: a = 10, b = 10
main2: a =
20
, b =
20
Po powrocie z funkcji fun() wartości zmiennych a i b nie zmieniły się, gdyż w funkcji
fun() pracowaliśmy na ich kopiach (posiadających takie same nazwy).
Przekazywanie parametrów do funkcji przez wskaźnik
Przekazywanie parametrów do funkcji przez wskaźnik polega na tym, że do funkcji
przekazywane są adresy zmiennych. Wszystkie operacje w funkcji wykonywane są zatem
na zmiennych z funkcji wywołującej (poprzez adres tych zmiennych).
Przykład nr 8.3
Przekazywanie parametrów do funkcji przez wskaźnik.
#include <stdio.h>
#include <conio.h>
void fun(
int *a
,
int *b
)
{
printf("fun1: a=%3d, b=%3d \n",
*a
,
*b
);
*a
= 10;
*b
= 10;
Informatyka 1 - Pracownia
Strona 5 z 9
© 2011 Jarosław Forenc
printf("fun2: a=%3d, b=%3d \n",
*a
,
*b
);
}
int main()
{
int
a
= 20,
b
= 20;
printf("main1: a = %3d, b = %3d \n",
a
,
b
);
fun(
&a
,
&b
);
printf("main2: a = %3d, b = %3d \n",
a
,
b
);
getch();
return 0;
}
Wynik działania programu:
main1: a = 20, b = 20
fun1: a = 20, b = 20
fun2: a = 10, b = 10
main2: a =
10
, b =
10
Po powrocie z funkcji fun() wartości zmiennych a i b zostały zmienione, gdyż do funkcji
fun() przekazane zostały adresy zmiennych a i b (&a, &b) i pracowaliśmy na zmiennych
poprzez ich adresy.
W funkcji main():
int a
;
- deklaracja zmiennej typu int,
a
- zmienna typu int,
&a
- adres zmiennej, a nie jej wartość.
W funkcji fun():
int *a;
- deklaracja zmiennej wskaźnikowej (wskaźnik na int),
a
- adres zmiennej typu int,
*a
- wartość zmiennej wskazywanej przez a.
Zmienne lokalne i zmienne globalne
Zmienne zadeklarowane w funkcjach są zmiennymi lokalnymi widzianymi tylko
w obrębie danej funkcji (bloku funkcyjnego) od miejsca, w którym zostały
zadeklarowane. Zmienne lokalne są zmiennymi automatycznymi.
Zmienne zadeklarowane poza funkcją main są zmiennymi globalnymi widzianymi
w całym programie od miejsca deklaracji (są to zmienne statyczne).
Jeśli zmienna globalna i lokalna mają takie same nazwy, to zmienna lokalna przesłania
widzialność zmiennej globalnej w danej funkcji.
Informatyka 1 - Pracownia
Strona 6 z 9
© 2011 Jarosław Forenc
#include ...
int a, b;
void f1()
{
float a, c;
...
}
int main()
{
int c, d;
...
}
zmienne a i b typu int są zmiennymi globalnymi,
zmienne a i c typu float są zmiennymi lokalnymi widzianymi
tylko w funkcji f1(),
zmienne c i d typu int są zmiennymi lokalnymi widzianymi
tylko w funkcji main(),
zmienna globalna b widziana jest w obu funkcjach: f1()
i main(),
zmienna globalna a widziana jest tylko w funkcji main(),
gdyż w funkcji f1() jej nazwa została przesłonięta przez
zmienną lokalną a typu float.
Przekazywanie tablicy jednowymiarowej (wektora) do funkcji
Przy przekazywaniu do funkcji wektora, w nagłówku funkcji umieszczamy typ
elementów wektora, jego nazwę i same nawiasy kwadratowe lub nawiasy kwadratowe
z rozmiarem wektora:
void zeruj(int tab[])
lub
void zeruj(int tab[5])
W wywołaniu funkcji podajemy natomiast tylko nazwę wektora:
zeruj(tab);
Przykład nr 8.4
Przekazywanie do funkcji tablicy jednowymiarowej (wektora).
#include <stdio.h>
#include <conio.h>
void zeruj(
int tab[5]
)
{
int i;
for(i=0; i<5; i++) tab[i] = 0;
}
int main()
{
int i, tab[5] = {1,2,3,4,5};
for(i=0; i<5; i++) printf("%3d",tab[i]);
printf("\n");
zeruj(tab);
for(i=0; i<5; i++) printf("%3d",tab[i]);
printf("\n");
getch();
return 0;
}
Informatyka 1 - Pracownia
Strona 7 z 9
© 2011 Jarosław Forenc
Wynikiem działania powyższego programu jest:
1 2 3 4 5
0 0 0 0 0
Do funkcji zeruj() jest przekazywany adres wektora. Wszystkie operacje dokonane na
wektorze w funkcji są uwzględnione po wyjściu z niej.
Przekazywanie tablicy dwuwymiarowej (macierzy) do funkcji
Przy przekazywaniu do funkcji tablicy dwuwymiarowej musimy koniecznie podać liczbę
wierszy i kolumn lub tylko liczbę kolumn:
void zeruj(int tab[2][3])
lub
void zeruj(int tab[][3])
W wywołaniu funkcji podajemy natomiast tylko nazwę tablicy:
zeruj(tab);
Przykład nr 8.5
Przekazywanie do funkcji tablicy dwuwymiarowej (macierzy).
#include <stdio.h>
#include <conio.h>
void zeruj(
int tab[][3]
)
{
int i, j;
for(i=0; i<2; i++)
for(j=0; j<3; j++)
tab[i][j] = 0;
}
void drukuj(
int tab[2][3]
)
{
int i, j;
for(i=0; i<2; i++)
{
for(j=0; j<3; j++)
printf("%3d",tab[i][j]);
printf("\n");
}
printf("\n");
}
int main()
{
int i, j, tab[2][3] = {{1,2,3},{4,5,6}};
drukuj(tab);
zeruj(tab);
drukuj(tab);
getch();
return 0;
}
Informatyka 1 - Pracownia
Strona 8 z 9
© 2011 Jarosław Forenc
Wynikiem działania powyższego programu jest:
1 2 3
4 5 6
0 0 0
0 0 0
Do funkcji zeruj() jest przekazywany adres macierzy. Wszystkie operacje dokonane na
macierzy w funkcji są uwzględnione po wyjściu z niej.
Przekazywanie struktur do funkcji
Struktury przekazywane są do funkcji tak jak każde inne zmienne podstawowych typów,
czyli przez wartość.
Przykład nr 8.6
Program zawierający funkcję obliczającą odległość dwóch punktów.
#include <stdio.h>
#include <conio.h>
#include <math.h>
struct punkt
{
int x, y;
};
float odleglosc(struct punkt pkt1, struct punkt pkt2)
{
return sqrt(pow(pkt2.x-pkt1.x,2)+pow(pkt2.y-pkt1.y,2));
}
int main()
{
struct punkt p1 = {2,3}, p2 = {-2,1};
float odl;
odl = odleglosc(p1,p2);
printf("Punkt nr 1: (%d,%d)\n",p1.x,p1.y);
printf("Punkt nr 2: (%d,%d)\n",p2.x,p2.y);
printf("Odleglosc = %g\n",odl);
getch();
return 0;
}
Rekurencyjne wywołanie funkcji
Rekurencyjne wywołanie funkcji polega na ponownym jej wywołaniu zanim skończyło
się jej poprzednie wywołanie.
Poniżej znajdują się trzy wersje funkcji obliczającej silnię liczby n.
Informatyka 1 - Pracownia
Strona 9 z 9
© 2011 Jarosław Forenc
Przykład nr 8.7 (a)
Funkcja obliczająca silnię liczby (wersja nierekurencyjna).
int silnia(int n)
{
int i, wynik = 1;
for (i=1; i<=n; i++)
wynik = wynik * i;
return wynik;
}
Przykład nr 8.7 (b)
Funkcja obliczająca silnię liczby (wersja rekurencyjna).
int silnia(int n)
{
if (n<=1)
return 1;
else
return n*silnia(n-1);
}
Przykład nr 8.7 (c)
Funkcja obliczająca silnię liczby (wersja rekurencyjna).
int silnia(int n)
{
return n ? n*silnia(n-1) : 1;
}