Menu

Funkcje w C: deklaracja, wywołanie i zwracanie wartości

Jak definiować i wywoływać funkcje w C: typ zwracany, parametry, wartości zwracane, funkcje void, dlaczego podział na funkcje ma znaczenie i pełny przykład zbudowany z małych, nazwanych kawałków.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Po co funkcje

Wszystko do tej pory mieszkało w main. To działa, dopóki program robi jedną rzecz, a potem pojawiają się naraz trzy problemy: ten sam blok kodu trafia w trzy miejsca, main rośnie ponad to, co da się ogarnąć w głowie, i nie ma jak nazwać, do czego służy dany kawałek kodu.

Funkcja rozwiązuje wszystkie trzy. Nadaje blokowi kodu nazwę, własne zmienne lokalne, listę wejść i jedną wartość do oddania.

square jest napisana raz i wywołana trzy razy, w tym raz na własnym wyniku. Funkcje wywołujesz już od dawna: printf jest funkcją, main też.

Budowa funkcji

returnType name(parameterList) {
    // ciało
    return value;
}

Cztery części:

  • Typ zwracany: typ wartości, którą funkcja oddaje. int, double, char, typ wskaźnikowy albo void dla "niczego".
  • Nazwa: podlega tym samym regułom co nazwa zmiennej i powinna mówić, co funkcja robi albo wytwarza.
  • Lista parametrów: wejścia, każde z własnym typem, oddzielone przecinkami. (void) oznacza, że funkcja nic nie przyjmuje.
  • Ciało: instrukcje w nawiasach klamrowych, zakończone return, chyba że typ zwracany to void.

Zauważ, że pi() nadal potrzebuje pustych nawiasów w miejscu wywołania: to one sprawiają, że jest to wywołanie, a nie samo wspomnienie nazwy. A printBanner(20); to osobna instrukcja, bo nie daje żadnej wartości do użycia.

Dla funkcji bez parametrów pisz (void), a nie (). W C te zapisy znaczą co innego, a () jest luźniejszy; więcej o tym za chwilę.

Wywołanie i powrót

Wywołanie przenosi sterowanie do funkcji, wykonuje jej ciało i wraca z wartością, która zastępuje wywołanie w otaczającym wyrażeniu.

return robi naraz dwie rzeczy: dostarcza wartość i od razu kończy funkcję. Nic, co stoi po return na tej samej ścieżce, się nie wykona, i dlatego styl wczesnego powrotu w larger działa: else nie jest potrzebne, bo dojście do ostatniej linii już oznacza, że a > b był fałszem.

Funkcja może mieć kilka instrukcji return. W jednym wywołaniu wykona tylko jedną z nich.

Funkcje void

Funkcja, która istnieje dla swojego efektu, a nie wartości, ma typ zwracany void: wypisywanie, zmiana czegoś przez wskaźnik, rysowanie.

Wewnątrz funkcji void samo return; kończy ją wcześniej. return someValue; jest tam błędem kompilacji, podobnie jak użycie wywołania w wyrażeniu: int x = printTable(7, 5); się nie skompiluje, i to system typów robi swoje.

Podział na funkcje: o to naprawdę chodzi

Funkcje nie służą głównie unikaniu powtórzeń. Służą zamianie jednej długiej procedury na kilka nazwanych kroków, tak żeby main czytało się jak opis programu, a nie jego implementacja.

main to teraz cztery linie wyjścia i jedno obliczenie. Każda funkcja pomocnicza robi jedną rzecz, ma nazwę, która mówi, co to za rzecz, i można ją przetestować osobno. Zwróć uwagę, że długość tablicy wędruje jako drugi parametr: po przekazaniu tablicy do funkcji sztuczka z sizeof już tam nie działa, bo tablica degraduje się do wskaźnika.

Trzy praktyczne zasady podziału pracy na funkcje:

  • Jedno zadanie na funkcję. Jeśli nazwa potrzebuje "i", to pewnie są dwie funkcje.
  • Bierz to, czego potrzebujesz, zwracaj to, co wytwarzasz. Funkcja, która czyta stan globalny, jest trudniejsza do zrozumienia i nie da się jej przetestować w izolacji.
  • Na tyle krótka, żeby objąć ją wzrokiem. Nie ma magicznej liczby linii, ale funkcja, która nie mieści się na ekranie, zwykle ukrywa w sobie mniejszą funkcję.

Funkcje wywołujące funkcje

Funkcja może wywołać każdą funkcję, która została już zadeklarowana, w tym te napisane przez ciebie:

main wywołuje report, która wywołuje sumEvens, która w pętli wywołuje isEven. Każde wywołanie dostaje własny, świeży zestaw zmiennych lokalnych, ułożony na stosie nad zmiennymi wywołującego, i porzucany po powrocie.

Definicje powyżej stoją w kolejności zależności, isEven przed sumEvens przed report, bo C wymaga, żeby funkcja była zadeklarowana przed wywołaniem. Wywołaj funkcję, która pojawia się niżej w pliku, a kompilator zgłosi niejawną deklarację. Ogólnym rozwiązaniem jest prototyp na początku pliku, który pozwala ułożyć definicje w dowolnej kolejności. Funkcja wywołująca samą siebie też jest poprawna i przydatna; to rekurencja.

main też jest funkcją

main to zwykła funkcja, którą środowisko uruchomieniowe wywołuje za ciebie. Jej typ zwracany int to kod wyjścia programu: 0 oznacza sukces, wartość niezerowa porażkę, a odczytują go skrypty powłoki i narzędzia budowania.

int main(void) {
    /* ... */
    return 0;
}

Od C99 dojście do końca main bez return niejawnie zwraca 0. To specjalna reguła, która dotyczy tylko main. Każda inna funkcja nie-void, która kończy się bez zwrócenia wartości, daje niezdefiniowane zachowanie, gdy wywołujący użyje wyniku.

Druga standardowa forma to int main(int argc, char *argv[]), która przyjmuje argumenty wiersza poleceń. Obie są poprawne; void main() nie jest, cokolwiek mówią stare tutoriale.

Częste błędy

  • Brak return w funkcji nie-void. Wywołujący odczyta wtedy śmieciową wartość. gcc -Wall ostrzega.
  • Deklarowanie w ciele zmiennej o tej samej nazwie co parametr. Przesłania ona parametr, a przypisanie, które miało trafić do wejścia, trafia donikąd.
  • Oczekiwanie, że funkcja zmieni swoje argumenty. C przekazuje wszystko przez wartość, więc funkcja dostaje kopie. void reset(int x) { x = 0; } niczego nie zmienia w miejscu wywołania; to temat następnej strony.
  • Pominięcie nawiasów w wywołaniu. printBanner; to poprawne wyrażenie, które oblicza adres funkcji i go porzuca. Kompiluje się, nic nie robi, a -Wall to zgłasza.
  • Pisanie () zamiast (void). Poprawne, ale w starszych dialektach C wyłącza sprawdzanie argumentów.

Najczęściej zadawane pytania

Jak zadeklarować funkcję w C?

Napisz typ zwracany, nazwę i listę parametrów w nawiasach, a potem ciało w nawiasach klamrowych: int add(int a, int b) { return a + b; }. Funkcja, która nic nie przyjmuje, używa (void), a taka, która nic nie zwraca, ma typ zwracany void.

Co robi return w funkcji w C?

Natychmiast kończy funkcję i oddaje wartość wywołującemu. Typ tej wartości musi pasować do zadeklarowanego typu zwracanego funkcji. W funkcji void możesz napisać samo return;, żeby wyjść wcześniej, albo je pominąć i pozwolić funkcji skończyć się na zamykającym nawiasie klamrowym.

Dlaczego main w C zwraca int?

Wartość zwracana przez main to kod wyjścia programu, który może odczytać system operacyjny i skrypty powłoki. return 0; oznacza sukces, a każda wartość niezerowa oznacza porażkę. Od C99 dojście do końca main bez instrukcji return niejawnie zwraca 0.

Czy funkcja w C może zwrócić więcej niż jedną wartość?

Nie bezpośrednio: return zwraca dokładnie jedną wartość. Typowe obejścia to zwrócenie struktury z kilkoma polami albo przekazanie wskaźników jako parametrów, przez które funkcja zapisze dodatkowe wyniki.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ