25 lipca 2008

linkowanie zewnętrzne a wewnętrzne

Po naciśnięciu magicznej kombinacji CTRL + F5 uruchamia się tajemna maszyneria, za pomocą której, z naszego, pisanego w pocie czoła, tekstu, wyłania się (nie)działający program. Zazwyczaj szczegóły jak to się dzieje,  że zwykły plik tekstowy zostaje zamieniony na coś co czasem przypomina wirtualną formę życia, jest mało istotne. Są jednak detale, które powinny obchodzić każdego (nie)przeciętnego programistę. 

Proces budowania programu dzieli się na dwa etapy: kompilacja i linkowanie.  Pierwszy etap to utworzenie pojedynczych jednostek translacji. Polega to na rekursywnym dołączaniu, przez preprocesor tekstu, do każdego pliku cpp, odpowiednich plików nagłówkowych (#include). Po wykonaniu tych operacji, następuje kompilacja i zostaje utworzony plik *.obj o takiej samej nazwie. Gdy powstaną wszystkie jednostki translacji, linkier (konsolidator) łączy w całość powstałe pliki obj, dając nam wynikowy program (etap drugi).

Nie zawsze jednak jest tak różowo. Kompilacja jak i linkowanie mogą zakończyć się błędami.   Błędy kompilacji są zazwyczaj dość proste do naprawienia - IDE pokazuje nam miejsce wystąpienia oraz przyczynę.  Gorzej jest z błędami podczas linkowania, jednak i te po krótkim czasie stają się oczywiste. Najczęściej spotykanym problemem, jest użycie niezaimplementowanej funkcji lub metody (unresolved external symbol). Czasem w pośpiechu, zapomnimy o samej main() (unresolved external symbol _main), lub utworzymy zły typ projektu i zamiast WinMain napiszemy znane z konsloli main() (unresolved external symbol _WinMain@16).  Czasem możemy natrafić jednak na problemy zwiazane z powonym zadeklarowaniem, lub zdefiniowaniem obiektów, zmiennych. Jak się przed tym ustrzec? Poznać kilka prostych zasad ;)

Nazwa jest konsolidowana (łączona) wewnętrznie, jeśli jest nazwą lokalną wewnątrz jej jednostki translacyjnej i podczas linkowania nie koliduje z identyczną nazwą, zdefiniowaną w innej jednosce translacyjnej. 

No tak. Oczywiste prawda? Prawda ;) Po krótce, jeśli zdefiniujemy jakąś zmienną łączoną wewnętrznie w pliku cpp w globalnej przestrzeni nazw, zmienna ta będzie widoczna tylko w tym pliku i nie wywoła kolizji podczas linkowania z innym plikiem, w którym jest zdefiniowana inna zmienna o tej samej nazwie. Łączenie zewnętrzne działa w odwrotny sposób - dana definicja nie może zostać powtórzona w innych jednostkach translacji, bo wywoła kolizje podczas linkowania. 

Jakie typy są w jaki sposób łączone?

Linkowne zewnętrzne są:

  • definicje zmienych 
  • definicje funkcji i metod niebedących inline
  • statyczne składowe klas
  • deklaracje z modyfikatorem extern

Łączone wewnętrznie są:

  • deklaracje i definicje klas
  • definicje typów za pomocą typedef
  • metody klas typu inline
  • globalne definicje statyczne
  • typy wyliczeniowe
  • stałe

Pewnie nie to wszystkie możliwości, jednak te najważniejsze. Wiedza ta czasem okaże się przydatna, w szczególności gdy korzystamy z zewnętrznych bibliotek, pisanych bez użycia przestrzeni nazw i "podstawowych zasad bezpieczeństwa" ;) .

4 komentarze:

  1. No, całkiem ładnie napisane ;) tylko dwa razy musiałem przeczytać zanim zrozumiałem :D. (aczkolwiek pierwszy raz czytałem jakoś o 2 w nocy :P)

    OdpowiedzUsuń
  2. Pod Twoim tajemniczym "globalne definicje statyczne" kryje się konstrukcja:

    static int ZmiennaGlobalna;

    która służy właśnie do definiowania zmiennych jako linkowane wewnętrznie, bo domyślnie są zewnętrznie. I tutaj słowo static działa zupełnie inaczej niż statyczne pola i metody klas albo statyczne zmienne lokalne wewnątrz funkcji/metod. Można to było jaśniej podkreślić :)

    OdpowiedzUsuń
  3. Sądziłem, że jest to w miarę oczywiste ;)

    OdpowiedzUsuń
  4. jak ktoś wie co to jest,to faktycznie jest oczywiste :P. A jak się nie wie, to nie jest oczywiste :P (powiedział filozoficznie thaven i oddał mocz za firankę :P)

    OdpowiedzUsuń