Wstęp W matematyce pod pojęciem funkcji rozumiemy twór, który pobiera pewną liczbę argumentów i zwraca wynikAby nie urażać matematyków sprecyzujmy, że chodzi o relację między zbiorami X i Y (X jest dziedziną, Y jest przeciwdziedziną) takie, że każdy element zbioru X jest w relacji z dokładnie jednym elementem ze zbioru Y.. Jeśli dla przykładu weźmiemy funkcję sin(x) to x będzie zmienną rzeczywistą, która określa kąt, a w rezultacie otrzymamy inną liczbę rzeczywistą - sinus tego kąta.
W C funkcja (czasami nazywana podprogramem, rzadziej procedurą) to wydzielona część programu, która przetwarza argumenty i ewentualnie zwraca wartość, która następnie może być wykorzystana jako argument w innych działaniach lub funkcjach. Funkcja może posiadać własne zmienne lokalne. W odróżnieniu od funkcji matematycznych, funkcje w C mogą zwracać dla tych samych argumentów różne wartości.
Po lekturze poprzednich części podręcznika zapewne mógłbyś podać kilka przykładów funkcji, z których korzystałeś. Były to np. funkcja printf(), drukująca tekst na ekranie, czy funkcja main(), czyli główna funkcja programu.
Główną motywacją tworzenia funkcji jest unikanie powtarzania kilka razy tego samego kodu. W poniższym fragmencie: for(i=1; i <= 5; ++i) { printf("%d ", i*i); } for(i=1; i <= 5; ++i) { printf("%d ", i*i*i);
for(i=1; i <= 5; ++i) { printf("%d ", i*i);
widzimy, że pierwsza i trzecia pętla for są takie same. Zamiast kopiować fragment kodu kilka razy (co jest mało wygodne i może powodować błędy) lepszym rozwiązaniem mogłoby być wydzielenie tego fragmentu tak, by można go było wywoływać kilka razy. Tak właśnie działają funkcje.
Innym, nie mniej ważnym powodem używania funkcji jest rozbicie programu na fragmenty wg ich funkcjonalności. Oznacza to, że jeden duży program dzieli się na mniejsze funkcje, które są "wyspecjalizowane" w wykonywaniu określonych czynności. Dzięki temu łatwiej jest zlokalizować błąd. Ponadto takie funkcje można potem przenieść do innych programów.
Formalnie wyróżniamy: deklaracja funkcji definicja funkcji wywołanie funkcji
Tworzenie funkcji Dobrze jest uczyć się na przykładach. Rozważmy następujący kod:
int iloczyn (int x, int y) { int iloczyn_xy; iloczyn_xy = x*y; return iloczyn_xy;
int iloczyn (int x, int y) to nagłówek funkcji, który opisuje, jakie argumenty przyjmuje funkcja i jaką wartość zwraca (funkcja może przyjmować wiele argumentów, lecz może zwracać tylko jedną wartość)Bardziej precyzyjnie można powiedzieć, że funkcja może zwrócić tylko jedną wartość typu prostego lub jeden adres do jakiegoś obiektu w pamięci.. Na początku podajemy typ zwracanej wartości - u nas int. Następnie mamy nazwę funkcji i w nawiasach listę argumentów.
Ciało funkcji (czyli wszystkie wykonywane w niej operacje) umieszczamy w nawiasach klamrowych. Pierwszą instrukcją jest deklaracja zmiennej - jest to zmienna lokalna, czyli niewidoczna poza funkcją. Dalej przeprowadzamy odpowiednie działania i zwracamy rezultat za pomocą instrukcji return. nazwa
Cel i zasady nadawania nazw:how-to-better-name-your-functions-and-variables by Friskovec Miha Wybierz słowo mające znaczenie (podaj kontekst): jednoznacznie i precyzyjnie opisywać koncept który nazywająNazwy zmiennych, notacje i konwencje nazewnicze - Mateusz Skalski Unikaj nazw ogólnych (takich jak tmp) Nazwa funkcji nie może być taka sama jak słowo kluczowe języka C oraz jak nazwa innej funkcji, która została wcześniej zdefiniowana w programieKurs języka C. Autor artykułu: mgr Jerzy Wałaszek Dołącz dodatkowe informacje do nazwy (użyj sufiksu lub prefiksu) Nie rób zbyt długich ani zbyt krótkich imion Używaj spójnego formatowania
Przykłady W nazwie są trzy człony określające:How I program C by Eskil Steenberg moduł. Tworzy to przestrzeń nazw (ang. namespace) object ( czego dotyczy co wykonuje funkcja
module_object_create()
Ten sposób podkreśla miejsce funkcji w strukturze modułu ( tak jak katalog)
Definicja Funkcję w języku C tworzy się następująco: typ identyfikator (typ1 argument1, typ2 argument2, typ_n argument_n) { /* instrukcje */
Oczywiście istnieje możliwość utworzenia funkcji, która nie posiada żadnych argumentów. Definiuje się ją tak samo, jak funkcję z argumentami z tą tylko różnicą, że między okrągłymi nawiasami nie znajduje się żaden argument lub pojedyncze słówko void - w definicji funkcji nie ma to znaczenia, jednak w deklaracji puste nawiasy oznaczają, że prototyp nie informuje jakie argumenty przyjmuje funkcja, dlatego bezpieczniej jest stosować słówko void.
Funkcje definiuje się poza główną funkcją programu (main). W języku C nie można tworzyć zagnieżdżonych funkcji (funkcji wewnątrz innych funkcji).
Struktura definicji return_type function_name( parameter_list ) { // body_of_the_function }
Stary sposób definiowania funkcji Zanim powstał standard ANSI C, w liście parametrów nie podawało się typów argumentów, a jedynie ich nazwy. Również z tamtych czasów wywodzi się oznaczenie, iż puste nawiasy (w prototypie funkcji, nie w definicji) oznaczają, że funkcja przyjmuje nieokreśloną liczbę argumentów. Tego archaicznego sposobu definiowania funkcji nie należy już stosować, ale ponieważ w swojej przygodzie z językiem C Czytelnik może się na nią natknąć, a co więcej standard nadal (z powodu zgodności z wcześniejszymi wersjami) dopuszcza taką deklarację to należy tutaj o niej wspomnieć. Otóż wygląda ona następująco:
typ_zwracany nazwa_funkcji(argument1, argument2, argumentn) typ1 argumenty /*, ... */; typ2 argumenty /*, ... */; /* ... */ { /* instrukcje */
Na przykład wcześniejsza funkcja iloczyn wyglądałaby następująco:
int iloczyn(x, y) int x, y; { int iloczyn_xy; iloczyn_xy = x*y; return iloczyn_xy;
Najpoważniejszą wadą takiego sposobu jest fakt, że w prototypie funkcji nie ma podanych typów argumentów, przez co kompilator nie jest w stanie sprawdzić poprawności wywołania funkcji. Naprawiono to (wprowadzając definicje takie jak je znamy obecnie) najpierw w języku C++, a potem rozwiązanie zapożyczono w standardzie ANSI C z 1989 roku.
Deklarowanie funkcji Czasami możemy chcieć przed napisaniem funkcji poinformować kompilator, że dana funkcja istnieje. Niekiedy kompilator może zaprotestować, jeśli użyjemy funkcji przed określeniem, jaka to funkcja, na przykład:
int a() { return b(0);
int b(int p) { if( p == 0 ) return 1; else return a();
int main() { return b(1);
W tym przypadku nie jesteśmy w stanie zamienić a i b miejscami, bo obie funkcje korzystają z siebie nawzajem. Rozwiązaniem jest wcześniejsze zadeklarowanie funkcji. Deklaracja funkcji (zwana czasem prototypem) to po prostu przekopiowana pierwsza linijka funkcji (przed otwierającym nawiasem klamrowym) z dodatkowo dodanym średnikiem na końcu. W naszym przykładzie wystarczy na samym początku wstawić: int b(int p); W deklaracji można pominąć nazwy parametrów funkcji: int b(int);
Bardzo częstym zwyczajem jest wypisanie przed funkcją main samych prototypów funkcji, by ich definicje umieścić po definicji funkcji main, np.:
int a(void); int b(int p);
int main() { return b(1);
int a() { return b(0);
int b(int p) { if( p == 0 ) return 1; else return a();
Z poprzednich rozdziałów pamiętasz, że na początku programu dołączaliśmy tzw. pliki nagłówkowe. Zawierają one właśnie prototypy funkcji i ułatwiają pisanie dużych programów. Dalsze informacje o plikach nagłówkowych zawarte są w rozdziale Tworzenie bibliotek.
argumenty funkcji liczba stała liczba argumentów bez argumentów ( zero argumentów) void 1 i więcej argumentów zmienna liczba argumentów (ang functions which take a variable number of arguments = variadic function) sposób wczytywaniageeksforgeeks: c-function-argument-return-values za pomocą wartości (ang. call by value) za pomocą wskaźnika (ang. call by reference)
Zobacz również argumenty I/O Zmienna liczba argumentów Zauważyłeś zapewne, że używając funkcji printf() lub scanf() po argumencie zawierającym tekst z odpowiednimi modyfikatorami mogłeś podać praktycznie nieograniczoną liczbę argumentów. Zapewne deklaracja obu funkcji zadziwi Cię jeszcze bardziej: int printf(const char *format, ...); int scanf(const char *format, ...);
Jak widzisz w deklaracji zostały użyte trzy kropki. Otóż język C ma możliwość przekazywania teoretycznie nieograniczonej liczby argumentów do funkcji (jedynym ograniczeniem jest rozmiar stosu programu). Cała zabawa polega na tym, aby umieć dostać się do odpowiedniego argumentu oraz poznać jego typ (używając funkcji printf, mogliśmy wpisać jako argument dowolny typ danych). Do tego celu możemy użyć wszystkich ciekawostek, zawartych w pliku nagłówkowym stdarg.h.
Załóżmy, że chcemy napisać prostą funkcję, która dajmy na to, mnoży wszystkie swoje argumenty (zakładamy, że argumenty są typu int). Przyjmujemy przy tym, że ostatni argument będzie 0. Będzie ona wyglądała tak:
#include
int mnoz (int pierwszy, ...) { va_list arg; int iloczyn = 1, t; va_start (arg, pierwszy); for (t = pierwszy; t; t = va_arg(arg, int)) { iloczyn *= t;
va_end (arg); return iloczyn;
va_list oznacza specjalny typ danych, w którym przechowywane będą argumenty, przekazane do funkcji. "va_start" inicjuje arg do dalszego użytku. Jako drugi parametr musimy podać nazwę ostatniego znanego argumentu funkcji. Makropolecenie va_arg odczytuje kolejne argumenty i przekształca je do odpowiedniego typu danych. Na zakończenie używane jest makro va_end - jest ono obowiązkowe!
Oczywiście, tak samo jak w przypadku funkcji printf() czy scanf(), argumenty nie muszą być takich samych typów. Rozważmy dla przykładu funkcję, podobną do printf(), ale znacznie uproszczoną:
#include
void wypisz(const char *format, ...) { va_list arg; va_start (arg, format); for (; *format; ++format) { switch (*format) { case 'i': printf("%d" , va_arg(arg, int)); break; case 'I': printf("%u" , va_arg(arg, unsigned)); break; case 'l': printf("%ld", va_arg(arg, int)); break; case 'L': printf("%lu", va_arg(arg, unsigned long)); break; case 'f': printf("%f" , va_arg(arg, double)); break; case 'x': printf("%x" , va_arg(arg, unsigned)); break; case 'X': printf("%X" , va_arg(arg, unsigned)); break; case 's': printf("%s" , va_arg(arg, const char *)); break; default : putc(*format);
va_end (arg);
Przyjmuje ona jako argument ciąg znaków, w których niektóre instruują funkcję, by pobrała argument i go wypisała. Nie przejmuj się jeżeli nie rozumiesz wyrażeń *format i ++format. Istotne jest to, że pętla sprawdza po kolei wszystkie znaki formatu.
Zobacz również: Funkcje o zmiennej liczbie argumentów (wariadyczne) ( ang. Variadic functions or varargs functions )Variadic functionsin cppreference gnu libc manual: Variadic-Functions makra ( ang. function macro) zdefiniowane w variadic w cppreference va_start - umożliwia dostęp do zmiennych argumentów funkcji va_arg - dostęp do następnego argumentu funkcji wariadycznej va_copy - makes a copy of the variadic function arguments (C99) va_end - ends traversal of the variadic function arguments typ : va_list - holds the information needed by va_start, va_arg, va_end, and va_copy Wiele starszych dialektów języka C zapewnia podobny, ale niekompatybilny mechanizm definiowania funkcji ze zmienną liczbą argumentów przy użyciu varargs.h ( legacy). Nie zaleca się używania
Pomoc: offline: W systemach uniksowych możesz uzyskać pomoc dzięki narzędziu man, przykładowo pisząc: man va_start
Wywoływanie funkcji Funkcje wywołuje się następująco:
identyfikator (argument1, argument2, argumentn);
Jeśli chcemy, aby przypisać zmiennej wartość, którą zwraca funkcja, należy napisać tak:
zmienna = funkcja (argument1, argument2, argumentn);
Przykładowo, mamy funkcję:
void pisz_komunikat() { printf("To jest komunikat\n");
Jeśli teraz ją wywołamy: pisz_komunikat; /* ŹLE */ pisz_komunikat(); /* dobrze */