C/C++: Kompilacja projektów - cz. 2

Kontynuujemy cykl na temat kompilacji projektów napisanych w językach C i C++. W pierwszej części zobaczyliśmy jak przeprowadzić kompilację projektu, który składa się z kilku plików źródłowych i nagłówkowych. Tym razem zobaczymy jak zastąpić część plików źródłowych wcześniej skompilowanym kodem.

Podział projektu na pliki źródłowe

Dla przypomnienia, w poprzedniej części cyklu napisaliśmy program, który wczytywał z wejścia tablicę pięciu liczb, sortował ją, a następtnie wypisywał posortowane elementy na wyjściu. Program podzieliliśmy na trzy pliki źródłowe:

  • app.c – zawierający funkcję main programu,
  • sort.c – zawierający funkcję sortującą tablicę,
  • arrayio.c – zawierający funkcje wczytujące i wypisujące wartości elementów tablicy.

Oprócz tego w projekcie znalazły się dwa pliki nagłówkowe: sort.h i arrayio.h zawierające prototypy funkcji zaimplementowanych w odpowiadających im plikach źródłowych.

Taki podział daje nam kilka korzyści, o których pisałem poprzednio: sprawia, że kod jest bardziej czytelny, pozwala nam powielać ten sam kod między różnymi projektami, a nawet korzystać z kodu dostarczonego przez innych programistów.

Osobna kompilacja plików

Teraz spróbujemy skompilować program w nieco inny sposób. Najpierw osobno skompilujemy pliki sort.c i arrayio.c. Pliki te nie zawierają funkcji main, więc nie da się z nich utworzyć plików wykonywalnych (w Windowsie z rozszerzeniem .exe). Możemy przeprowadzić jednak kompilację do tzw. plików obiektowych. Robimy to wywołując następujące polecenia:

 

gcc -c -o sort.o sort.c

gcc -c -o arrayio.o arrayio.c

Dodanie do wywołania gcc flagi -c powoduje kompilację do plików obiektowych.

Teraz spróbujmy skompilować plik app.c bez dołączania do niego dodatkowych plików. Robimy to komendą:

 

gcc -o app.exe app.c

Powinny się ukazać komunikaty o błędach. W moim przypadku jeden z nich wyglądał np. tak:

d:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: C:\Users\krasz\AppData\Local\Temp\ccq8OxyY.o:app.c:(.text+0x1e): undefined reference to `read_array’

Zwróć uwagę na dwie rzeczy. Pierwsza z to nazwa błędu: „undefined reference”. Tego typu błąd polega na tym, że nasz program ma korzystać z jakiejś funkcji (w tym przypadku read_array), ale nie dostarczyliśmy nigdzie jej implementacji (kodu). 

Druga sprawa to to, że błąd zgłosił nam program o nazwie ld.exe. Jest to tzw. linker (albo po polsku konsolidator). O linkerze i procesie kompilacji napiszę więcej w następnych częściach cyklu. Na razie warto wiedzieć, że skoro linker w ogóle się uruchomił to nasz plik app.c został poprawnie skompilowany. 

Jak to w ogóle możliwe? Dlaczego plik się skompilował skoro brakuje w nim implementacji kilku funkcji? Otóż, żeby skompilować naszą funkcję main kompilator nie musi znać kodu funkcji, z których ona korzysta (np. read_array). Wystarczy mu informacja o tym jakie mają one przyjmować argumenty i jakie mają zwracać wartości. Ta informacja jest zawarta w prototypach funkcji umieszczonych w plikach nagłówkowych. I to wystarcza do kompilacji pliku app.c.

Utworzenie pliku app.exe wymaga oczywiście dostarczenia kodu wszystkich brakujących funkcji. Ponieważ tego nie zrobiliśmy to linker zwraca nam błąd.

Na koniec skompilujmy program poprawnie dodając wcześniej utworzone pliki obiektowe. Wywołujemy polecenie:

gcc -o app.exe app.c sort.o arrayio.o

Teraz powinien powstać plik app.exe. Możesz na koniec przetestować działanie programu. Poniższy screen przedstawia wykonanie tego ćwiczenia w Visual Studio Code.

Problemy z plikami źródłowymi

Dołączanie do projektu dodatkowych plików źródłowych zawierających użyteczne funkcji wydaje się bardzo dobrym pomysłem, ale ma też swoje wady. Oto one:

  1. Długi czas kompilacji. Wyobraźmy sobie, że zmieniliśmy coś w pliku app.c. Musimy ponownie skompilować teraz cały projekt (wszystkie trzy pliki) pomimo tego, że zmiany dotyczą tylko części kodu. W naszym przypadku nawet tego nie zauważymy, ale w przypadku bardziej skomplikowanych projektów czas kompilacji może się przez to znacząco wydłużyć.
  2. Brak możliwości ukrycia kodu.  Wiele firm opiera swój biznes o sprzedaż bibliotek programistycznych. Gbyby takie biblioteki były dostarczane w formie plików z kodem źródłowym to takie firmy upubliczniałyby masę swojego know-how. Nie jest to najlepsze rozwiązanie.
  3. Problemy z kompilacją. Często skompilowanie jakiegoś fragmentu kodu wymaga odpowiedniej konfiguracji projektu. Chcąc przekazać bibliotekę funkcji innym programistom w formie plików źródłowych trzeba w takiej sytuacji dołączyć instrukcję jak te pliki poprawnie skompilować. Często nawet mając takie instrukcje trzeba się nagimnastykować aby poprawnie zbudować projekt.

Prekompilacja

Rozwiązaniem powyższych problemów jest skompilowanie fragmentów kodu (np. zbioru funkcji jak w naszym przykładzie) i dostarczanie tak właśnie skompilowanych fragmentów, które następnie mogą być dołączone do wynikowego programu za pomocą linkera.

Czy to rozwiązanie ma jakieś wady? Jak to zwykle bywa – nie ma rozwiązań idealnych. Dysponując kodem źródłowym możemy go skompilować tak, aby działał na różnych systemach operacyjnych a nawet procesorach. Jeżeli dysponujemy plikami binarnymi (przykładem takich plików są pliki z rozszerzeniem .o z naszego przykładu) możemy je wykorzystać tylko dla procesora i systemu operacyjnego na jaki zostały przygotowane.

Podsumowanie

To tyle na temat dołączania do kompilacji plików binarnych. Ponieważ przy podejściu które przećwiczyliśmy kompilacja robi się skomplikowania (mamy do wykonania trzy komendy), w następnej części zobaczymy jak możemy ją zautomatyzować przy pomocy programu GNU Make.

Maciej Kraszewski

Maciej Kraszewski

Inżynier, menedżer R&D i nauczyciel akademicki. Uwielbiam zajmować się tworzeniem nowych technologii, zdobywaniem nowej wiedzy i dzieleniem się swoim doświadczeniem z innymi. Specjalizuję się w zagadnieniach przetwarzania obrazu i widzenia maszynowego.
Szukasz dobrych materiałów o projektowaniu elektroniki?

Załóż darmowe konto na naszej platformie i odbierz pakiet materiałów edukacyjnych.

Zakładając konto zgadzasz się na przesyłanie Ci treści marketingowych przez IT20 sp. z o.o. zgodnie z dostępną na stronie Polityką Prywatności. Możesz wycofać zgodę w każdej chwili.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Szukasz dobrych materiałów o projektowaniu elektroniki?

Załóż darmowe konto na naszej platformie i odbierz pakiet materiałów edukacyjnych.

Zakładając konto zgadzasz się na przesyłanie Ci treści marketingowych przez IT20 sp. z o.o. zgodnie z dostępną na stronie Polityką Prywatności. Możesz wycofać zgodę w każdej chwili.

Zaprojektuj PCB

Jak przejść od zera do projektowania profesjonalnych obwodów drukowanych?

Programowanie w języku C

Jak przejść od napisania pierwszego programu komputerowego do wykorzystania zaawansowanych metod programowania?

Projektowanie układów elektronicznych

Jak działają i jak projektować poprawnie działające układy elektroniczne?
Zapisz się na listę mailową i odbierz swoje bonusy!

Więcej treści na temat elektroniki i robotyki, darmowe e-booki i dostęp do minikursów on-line. Otrzymasz to wszystko zapisując się na naszą listę mailową.