C/Biblioteki
Spis treści |
[edytuj] Czym jest biblioteka
Biblioteka jest to zbiór funkcji, które zostały wydzielone po to, aby dało się z nich korzystać w wielu programach. Ułatwia to programowanie - nie musimy np. sami tworzyć funkcji printf. Każda biblioteka posiada swoje pliki nagłówkowe, które zawierają deklaracje funkcji bibliotecznych oraz często zawarte są w nich komentarze, jak używać danej funkcji. W tej części podręcznika nauczymy się tworzyć nasze własne biblioteki.
[edytuj] Jak zbudowana jest biblioteka
Każda biblioteka składa się z co najmniej dwóch części (oczywiście w praktyce nie istnieje górny limit):
- pliku nagłówkowego z deklaracjami funkcji (plik z rozszerzeniem .h)
- pliku źródłowego, zawierającego ciała funkcji (plik z rozszerzeniem .c)
[edytuj] Budowa pliku nagłówkowego
Oto najprostszy możliwy plik nagłówkowy:
#ifndef PLIK_H #define PLIK_H /* tutaj są wpisane deklaracje funkcji */ #endif
Zapewne zapytasz się na co komu instrukcje #ifndef, #define oraz #endif. Otóż często się zdarza, że w programie korzystamy z plików nagłówkowych, które dołączają się wzajemnie. Oznaczałoby to, że w kodzie programu kilka razy pojawiła by się zawartość tego samego pliku nagłówkowego. Instrukcja #ifndef i #define temu zapobiega. Dzięki temu kompilator nie musi kilkakrotnie kompilować tego samego kodu.
W plikach nagłówkowych często umieszcza się też definicje typów, z których korzysta biblioteka albo np. makr.
[edytuj] Budowa najprostszej biblioteki
Załóżmy, że nasza biblioteka będzie zawierała jedną funkcję, która wypisuje na ekran tekst "pl.Wikibooks". Utwórzmy zatem nasz plik nagłówkowy:
#ifndef WIKI_H #define WIKI_H #include <stdio.h> void wiki (void) {printf("pl.Wikibooks\n");} #endif
Należy pamiętać, o podaniu void w liście argumentów funkcji nie przyjmujących argumentów. O ile przy definicji funkcji nie trzeba tego robić (jak to często czyniliśmy w przypadku funkcji main) o tyle w prototypie brak słówka void oznacza, że w prototypie nie ma informacji na temat tego jakie argumenty funkcja przyjmuje.
Plik nagłówkowy zapisujemy jako "wiki.h".
Ważne jest dołączenie na początku pliku nagłówkowego. Dlaczego? Plik nagłówkowy zawiera deklaracje naszych funkcji - jeśli popełniliśmy błąd i deklaracja nie zgadza się z definicją, kompilator od razu nas o tym powiadomi. Oprócz tego plik nagłówkowy może zawierać definicje istotnych typów lub makr. Napiszmy nasz program:
#include "wiki.h" int main () { wiki(); return 0; }
Zapiszmy program jako "main.c" Teraz musimy odpowiednio skompilować nasz program:
gcc main.c -o main
Uruchamiamy nasz program:
./main pl.Wikibooks
Jak widać nasza pierwsza biblioteka działa.
[edytuj] Zmiana dostępu do funkcji i zmiennych (static i extern)
Język C, w przeciwieństwie do swego młodszego krewnego - C++ nie posiada praktycznie żadnych mechanizmów ochrony kodu biblioteki przed modyfikacjami. C++ ma w swoim asortymencie m.in. sterowanie uprawnieniami różnych elementów klasy. Jednak programista, piszący program w C nie jest tak do końca bezradny. Autorzy C dali mu do ręki dwa narzędzia: extern oraz static. Pierwsze z tych słów kluczowych informuje kompilator, że dana funkcja lub zmienna istnieje, ale w innym miejscu, i zostanie dołączona do kodu programu w czasie łączenia go z biblioteką.
extern przydaje się, gdy zmienna lub funkcja jest zadeklarowana w bibliotece, ale nie jest udostępniona na zewnątrz (nie pojawia się w pliku nagłówkowym). Przykładowo:
/* biblioteka.h */ #ifndef BIBLIOTEKA_H #define BIBLIOTEKA_H extern char zmienna_dzielona[]; #endif /* biblioteka.c */ #include "biblioteka.h" char zmienna_dzielona[] = "Zawartosc"; /* main.c */ #include <stdio.h> #include "biblioteka.h" int main() { printf("%s\n", zmienna_dzielona); return 0; }
Gdybyśmy tu nie zastosowali extern, kompilator (nie linker) zaprotestowałby, że nie zna zmiennej zmienna_dzielona. Próba dopisania deklaracji char zmienna_dzielona[]; stworzyłaby nową zmienną i utracilibyśmy dostęp do interesującej nas zawartości.
Odwrotne działanie ma słowo kluczowe static użyte w tym kontekście (użyte wewnątrz bloku tworzy zmienną statyczną, więcej informacji w rozdziale Zmienne). Może ono odnosić się zarówno do zmiennych jak i do funkcji globalnych. Powoduje, że dana zmienna lub funkcja jest niedostępna na zewnątrz biblioteki[1]. Możemy dzięki temu ukryć np. funkcje, które używane są przez samą bibliotekę, by nie dało się ich wykorzystać przez extern.
Przypisy
- ↑ Tak naprawdę całe "ukrycie" funkcji polega na zmianie niektórych danych w pliku z kodem binarnym danej biblioteki (pliku .o), przez co linker powoduje wygenerowanie komunikatu o błędzie w czasie łączenia biblioteki z programem.