Funkcje i nagłówki
„Poznamy metody podziału kodu, co pozwoli łatwiej nim zarządzać oraz wyodrębniać moduły do wielokrotnego użytku.”
Pisząc większe programy, bardzo szybko dochodzimy do momentu, w którym pojedyńczy plik z setkami linii kodu przestaje być wygodny w utrzymaniu. Właśnie tutaj kluczową rolę odgrywają funkcje oraz pliki nagłówkowe. Pozwalają one porządkować kod, dzielić go na logiczne części i ponownie wykorzystywać w innych projektach.
Funkcje
Funkcja to wydzielony fragment kodu, który ma nazwę, może (ale nie musi) przyjmować argumenty oraz może (ale nie musi) zwracać wartość. Dzięki funkcjom unikamy powtarzania kodu i poprawiamy jego czytelność. Najczęściej spotkasz się z dwoma podstawowymi rodzajami funkcji: funkcjami zwracającymi wartość oraz funkcjami typu void, które nie zwracają wartości.
Poniżej przykład funkcji typu int o nazwie suma. Typ funkcji określa rodzaj zwracanej wartości, a sama wartość jest przekazywana przez zmienną podaną po słowie return. Zwracana wartość musi być zgodna z typem zadeklarowanym w nagłówku funkcji. W nawiasach podawane są argumenty funkcji — tutaj a i b.
int suma(int a, int b) {
int wynik;
wynik = a + b;
return wynik;
}
Przykładowa funkcja typu void — czyli taka, która nie zwraca wartości. W tym przypadku nie jest wymagane użycie słowa return, jednak jego obecność może poprawić czytelność kodu. Do funkcji void, jak i do każdej innej, możemy przekazywać argumenty.
void hello(int a, int b) {
if(a+b > 5){
printf("Hello world!\n");
}
}
Wywoływanie funkcji
W celu użycia funkcji trzeba ją wywołać, podając jej nazwę oraz argumenty – mogą to być bezpośrednio wartości lub zmienne wykorzystywane w programie.
Przykładowe wywołanie funkcji hello:
hello(4, 7);
Dla funkcji suma możemy od razu zapisać zwracany wynik do zmiennej:
int zmienna = suma(4, 7);
Deklaracja vs definicja funkcji
Musimy jeszcze rozróżnić deklarację funkcji od jej definicji. Deklaracja informuje kompilator, że funkcja o danej nazwie istnieje w naszym programie. Najczęściej umieszcza się ją na początku pliku programu lub w pliku nagłówkowym. Definicja natomiast zawiera ciało funkcji, czyli kod, który ma się wykonać.
Deklaracja:
int suma(int a, int b);
Definicja:
int suma(int a, int b) {
int wynik;
wynik = a + b;
return wynik;
}
Wywołanie:
int zmienna = suma(4, 7);
Przekazywanie argumentów – kopia, nie oryginał
W języku C argumenty funkcji są przekazywane przez wartość, co oznacza, że funkcja otrzymuje kopię przekazanej zmiennej, a nie jej oryginał. Każda zmiana dokonana na argumencie wewnątrz funkcji nie wpływa na zmienną w miejscu jej wywołania.
void zmien(int x) {
x = 10;
}
int main() {
int a = 5;
zmien(a);
printf("%d\n", a); // nadal 5
}
Jeżeli chcemy, aby funkcja mogła modyfikować oryginalną wartość, musimy przekazać adres zmiennej, czyli użyć wskaźników. O tym będzie mowa w przyszłości — teraz jedynie krótki przykład:
void zmien(int *x) {
*x = 10;
}
int main() {
int a = 5;
zmien(&a);
printf("%d\n", a); // 10
}
Uwaga:
Tablice w C są przekazywane do funkcji jako wskaźniki — dlatego ich zawartość można modyfikować bez użycia operatora&.
Zakres zmiennych i zmienne globalne
Zmienne mogą mieć różny zakres widoczności:
- lokalne – widoczne tylko w obrębie funkcji, w której zostały zadeklarowane,
- globalne – widoczne w całym pliku lub nawet w wielu plikach (jeśli użyjemy
extern).
Przykład zmiennej globalnej:
int licznik = 0; // zmienna globalna
void inkrementuj() {
licznik++;
}
Nadmierne używanie zmiennych globalnych może prowadzić do błędów i trudności w utrzymaniu kodu.
Słowo kluczowe static
- Zmienne statyczne w funkcji zachowują swoją wartość między wywołaniami. Zmienna nie jest na nowo inicjalizowana, tylko przechowuje wartość po ostatnim wywołaniu funkcji.
void licz() {
static int i = 0;
i++;
printf("%d\n", i);
}
- Funkcje statyczne są widoczne tylko w pliku, w którym zostały zdefiniowane:
static void pomocnicza() {
// kod niewidoczny poza tym plikiem
}
Funkcje rekurencyjne
Funkcja rekurencyjna wywołuje samą siebie. Bardzo przydatne przy problemach takich jak silnia, ciągi liczb Fibonacciego czy przeszukiwanie drzew.
int silnia(int n) {
if (n <= 1)
return 1;
return n * silnia(n - 1);
}
Należy pamiętać o warunku zakończenia, inaczej program skończy się przepełnieniem stosu.
Makra
Makra pozwalają na tworzenie stałych i prostych funkcji tekstowych:
#define PI 3.14159
#define MAX(a,b) ((a) > (b) ? (a) : (b))
Makra są bez typów, więc trzeba uważać na kolejność działań i nawiasy.
Nagłówki
#include
Dyrektywa #include wstawia zawartość innego pliku:
#include <stdio.h> // biblioteka standardowa
#include "moj.h" // własny nagłówek
< >– szukanie w katalogach systemowych" "– najpierw szukanie w katalogu projektu
#define
Służy do tworzenia makr, czyli stałych i funkcji tekstowych (opisane wyżej).
#ifdef / #ifndef
Pozwalają na warunkową kompilację, np. ochronę przed wielokrotnym włączeniem nagłówka:
#ifndef MOJ_H
#define MOJ_H
void funkcja();
#endif
Można też użyć
#pragma once— prostsze, ale mniej przenośne.
extern
Umożliwia użycie zmiennych i funkcji z innych plików:
// plik1.c
int globalna = 10;
// plik2.c
extern int globalna;
Podsumowanie
Funkcje i nagłówki to fundament każdego większego programu w C. Funkcje dzielą kod na logiczne, wielokrotnego użytku fragmenty, zwiększają czytelność i ułatwiają debugowanie. Nagłówki porządkują interfejsy między plikami i pozwalają współdzielić deklaracje funkcji oraz stałe. Zakres zmiennych, static, extern pozwalają kontrolować widoczność i czas życia danych. Makra i rekurencja oferują wygodne sposoby na automatyzację powtarzalnych operacji. Przekazywanie argumentów w C domyślnie przez wartość, modyfikacja oryginału wymaga wskaźników.
Dzięki przemyślanemu stosowaniu tych elementów Twój kod staje się czytelny, modularny i łatwy do utrzymania, nawet przy dużych projektach.
Powiązane wpisy
Wskaźniki i zarządzanie pamięcią
„Zajrzyjmy w głąb pamięci naszych komputerów i dowiemy się jak nią zarządzać.”
Pętle i instrukcje warunkowe
„Pętle oraz instrukcje warunkowe to podstawowe elementy sterujące przebiegiem programu w języku C.”
Operatory arytmetyczne, bitowe, relacyjne i logiczne
Poznamy dostępne operatory w języku C i przyjrzymy się ich wykorzystaniu w praktyce.