C/Biblioteki

Z Wikibooks, biblioteki wolnych podręczników.
< C
Skocz do: nawigacja, szukaj

Czym jest biblioteka[edytuj]

Biblioteka [1]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.

Cechy biblioteki :

  • składa się z dwóch plików : jeden nagłówkowy ( żródłowy) i jeden binarny ( skompilowany )
  • zawiera funkcje (deklaracje w nagłówkowym i definicje w binarnym )

typy[edytuj]

Typy bibliotek sposobu wykorzystania  :

  • statyczne ( ang. static library or statically-linked library )
    • windows : .lib lub .obj
    • Unix : .a lub .o
  • dynamiczne[2]
    • biblioteka łączona dynamicznie,
      • Unix : blioteka współdzielona (ang. shared library[3][4], shared object) .so, ścieżki poszukiwań plików bibliotek zapisane są w pliku /etc/ld.so.conf oraz w zmiennej środowiskowej $LD_LIBRARY_PATH.
      • Windows  : .dll
    • biblioteki ładowane dynamicznie

Typy bibliotek wg autora :

Ścieżka wyszukiwania[edytuj]

Gcc w sytemie Linuks wyszukuje nagłówki #include <file> w kilku standardowych katalogach: [5][6]

 /usr/local/include
 libdir/gcc/target/version/include
 /usr/target/include
 /usr/include

Możemy to sprawdzić za pomocą przełączników przy kompilacji :


gcc c.c -v -c 

przykładowy wynik :

ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/4.8/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.


Zmienna środowiskowa LD_LIBRARY_PATH zawiera ścieżki do katalogów bibliotek. Te katalogi kompilator będzie przeszukiwał w trakcie kompilacji:[7]

LD_LIBRARY_PATH

W celu dopisania ścieżki do własnej biblioteki ustaw wartość zmiennej środowiskowej, na przykład w konsoli wpisz:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib32:/usr/lib32

gdzie /lib32 i /usr/lib32 są dopisywanymi ścieżkami do bibliotek (plików *.so). Jak widzimy w przykładzie, ścieżki oddzielamy dwukropkiem.


Sprawdzamy jakie ścieżki są przeszukiwane :[8]


 echo | gcc -Wp,-v -x c++ - -fsyntax-only

przykładowy wynik :

ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/4.8"
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/4.8
 /usr/include/x86_64-linux-gnu/c++/4.8
 /usr/include/c++/4.8/backward
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

Jak zbudowana jest biblioteka[edytuj]

Kod źródłowy każdej biblioteki 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)

Biblioteka po skompilowaniu składa się z dwóch plików :

  • nagłówkowy ( żródłowy)
  • binarny ( skompilowany )

Budowa pliku nagłówkowego[edytuj]

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.

Budowa najprostszej biblioteki[edytuj]

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.


Zmiana dostępu do funkcji i zmiennych (static i extern)[edytuj]

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[9]. 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