Nagłówek C / C++ i pliki implementacyjne: jak działają?

To jest prawdopodobnie głupie pytanie, ale od dłuższego czasu Szukałem tu i w sieci i nie mogłem znaleźć jasnej odpowiedzi(Czy moje due diligence googling).

Więc jestem nowy w programowaniu... Moje pytanie brzmi: skąd główna funkcja wie o definicjach funkcji (implementacjach) w innym pliku?

Ex. Powiedz, że mam 3 pliki

  • main.cpp
  • moja funkcja.cpp
  • moja funkcja.hpp

//main.cpp

#include "myfunction.hpp"
int main() {
  int A = myfunction( 12 );
  ...
}

-

//myfunction.cpp

#include "myfunction.hpp"
int myfunction( int x ) {
  return x * x;
}

-

//myfunction.hpp

int myfunction( int x );

-

Rozumiem, w jaki sposób preprocesor zawiera kod nagłówka, ale skąd nagłówek i główna funkcja wiedzą, że definicja funkcji istnieje, a tym bardziej ją wykorzystują?

Przepraszam, jeśli to nie jest jasne lub bardzo się mylę co do czegoś nowego tutaj

Author: emlai, 2012-02-10

7 answers

Plik nagłówkowy deklaruje funkcje/klasy - tzn. mówi kompilatorowi podczas kompilacji pliku .cpp jakie funkcje/klasy są dostępne.

Plik .cpp definiuje te funkcje - tzn. kompilator kompiluje kod i dlatego tworzy rzeczywisty kod maszynowy do wykonywania tych działań, które są zadeklarowane w odpowiednim pliku .hpp.

W twoim przykładzie main.cpp zawiera plik .hpp. Preprocesor zastępuje #include zawartością .hpp Plik. Ten plik mówi kompilatorowi, że funkcja {[7] } jest zdefiniowana gdzie indziej i pobiera jeden parametr (an int) i zwraca int.

Więc po kompilacji main.cpp do pliku obiektowego (.o extension) zaznacza w tym Pliku, że wymaga funkcji myfunction. Gdy kompilujesz myfunction.cpp do pliku obiektowego, plik obiektowy zawiera notatkę, że ma definicję myfunction.

Wtedy, gdy dojdzie do połączenia dwóch plików obiektowych razem w plik wykonywalny, linker wiąże końcówki-tzn. main.o używa {[7] } zgodnie z definicją w myfunction.o.

Mam nadzieję, że to pomoże

 56
Author: Ed Heal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 08:11:02

Musisz zrozumieć, że kompilacja jest operacją dwuetapową z punktu widzenia użytkownika.


Pierwszy krok: kompilacja obiektów

Podczas tego kroku, Twoje *.pliki c są indywidualnie skompilowane do oddzielnych plików obiektowych. Oznacza to, że gdy main.cpp jest skompilowany, nie wie nic o Twojej mojej funkcji.cpp . Wie tylko, że zadeklarujesz , że funkcja o tym podpisie: int myfunction( int x ) istnieje w inny plik obiektowy.

Kompilator zachowa odwołanie do tego wywołania i zamieści je bezpośrednio w pliku obiektowym. Plik obiektu będzie zawierał "muszę wywołać mojafunction z an int i powróci do mnie z an int. Przechowuje indeks wszystkich extern wywołań, aby móc później połączyć się z innymi.


Drugi etap: łączenie

Podczas tego kroku, linker spojrzy na wszystkie te indeksy twojego plików obiektowych i spróbuje rozwiązać zależności w tych plikach. Jeśli go nie ma, dostaniesz z niego słynną undefined symbol XXX. Następnie przełoży te odniesienia na rzeczywisty adres pamięci w pliku wynikowym: albo binarnym, albo bibliotecznym.


I wtedy możesz zacząć pytać, Jak to jest możliwe, aby to zrobić z gigantycznym programem, takim jak pakiet biurowy, który ma mnóstwo metod i obiektów ? Używają mechanizmu shared library. Znasz ich ze swoimi.dll " i / lub '. so ' pliki, które masz na swojej stacji roboczej Unix / Windows. Pozwala to odłożyć rozwiązanie niezdefiniowanego symbolu do czasu uruchomienia programu.

Pozwala nawet rozwiązywać niezdefiniowany symbol na żądanie , za pomocą dl* funkcji.

 14
Author: Coren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 08:23:40

1. Zasada

Kiedy piszesz:

int A = myfunction(12);

To jest przetłumaczone na:

int A = @call(myfunction, 12);

Gdzie @call może być postrzegane jako szukanie słownika. A jeśli pomyślisz o analogii słownikowej, to na pewno wiesz o słowie (smogashboard ?) przed poznaniem jego definicji. Wszystko, czego potrzebujesz, to to, że w czasie wykonywania, definicja być w słowniku.

2. Punkt na ABI

Jak to działa@call ? Na ABI. ABI jest sposobem, który opisuje wiele rzeczy, a wśród nich jak wykonać wywołanie danej funkcji (w zależności od jej parametrów). Umowa wywołania jest prosta: po prostu mówi, gdzie można znaleźć każdy z argumentów funkcji(niektóre będą w rejestrach procesora, inne na stosie).

Dlatego @call faktycznie robi:

@push 12, reg0
@invoke myfunction

I definicja funkcji wie, że jej pierwszy argument (x ) znajduje się w reg0.

3. Ale Ja choć słowniki były dla języków dynamicznych ?

I masz rację, do pewnego stopnia. Języki dynamiczne są zazwyczaj implementowane z tabelą hash do wyszukiwania symboli, która jest dynamicznie wypełniana.

Dla C++, kompilator przekształci jednostkę tłumaczenia (z grubsza mówiąc, wstępnie przetworzony plik źródłowy) w obiekt (.o lub .obj w ogóle). Każdy obiekt zawiera tabelę symboli, do których się odwołuje, ale dla których definicja nie jest znana:

.undefined
[0]: myfunction

Wtedy łącznik połączy obiekty i pogodzi symbole. W tym momencie istnieją dwa rodzaje symboli:

    W 1995 roku biblioteka została przekształcona w bibliotekę bibliotekarską, a w 1999 roku w bibliotekę bibliotekarską.]}
  • te, które znajdują się poza biblioteką i których adres jest zupełnie nieznany do czasu uruchomienia.

Oba mogą być traktowane w ten sam sposób.

.dynamic
[0]: myfunction at <undefined-address>

I wtedy kod będzie odwoływał się do wyszukiwania wpis:

@invoke .dynamic[0]

Gdy Biblioteka zostanie załadowana (na przykładDLL_Open), runtime w końcu pozna gdzie symbol jest mapowany w pamięci i nadpisze <undefined-address> rzeczywistym adresem (dla tego uruchomienia).

 5
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 08:29:15

Jak sugerowano w komentarzu Matthieu M., zadaniem linkera jest znalezienie odpowiedniej "funkcji" we właściwym miejscu. Kroki kompilacji to w przybliżeniu:

  1. kompilator jest wywoływany dla każdego pliku cpp i tłumaczony na plik obiektowy (kod binarny) z tabelą symboli , która kojarzy nazwa funkcji (nazwy są w c++ zniekształcone) do ich lokalizacji w plik obiektowy.
  2. linker jest wywoływany tylko jeden raz: z każdym plikiem obiektu w parametr. Rozwiąże lokalizacja wywołania funkcji z jednego obiektu file to another thanks to symbol tables . Jedna funkcja main () musi istnieć gdzieś. Ostatecznie powstaje binarny plik wykonywalny kiedy linker znalazł wszystko, czego potrzebuje.
 4
Author: yves Baumes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 08:13:19

Preprocesor zawiera zawartość plików nagłówkowych w plikach cpp (pliki cpp nazywane są jednostką tłumaczenia). Podczas kompilacji kodu każda jednostka translacyjna jest sprawdzana pod kątem błędów semantycznych i składniowych. Obecność definicji funkcji w jednostkach translacyjnych nie jest brana pod uwagę. .pliki obj są generowane po kompilacji.

W następnym kroku, gdy pliki obj są połączone. definicja funkcji (funkcji Członkowskich dla klas), które są używane otrzymuje / align = "left" / Jeśli funkcja nie zostanie znaleziona, zostanie wyświetlony błąd linkera.

W twoim przykładzie, jeśli funkcja nie została zdefiniowana w mojafunkcji.cpp, kompilacja i tak będzie bez problemu. Błąd zostanie zgłoszony w kroku łączenia.

 4
Author: Ram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 08:14:13

int myfunction(int); jest prototypem funkcji. Deklarujesz za jej pomocą funkcję tak, aby kompilator wiedział, że wywołujesz tę funkcję podczas pisania myfunction(0);.

Oraz skąd nagłówek i główna funkcja wiedzą o istnieniu definicji funkcji?
Cóż, to jest Zadanie linkera.

 2
Author: LihO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 08:10:56

Podczas kompilacji programu preprocesor dodaje kod źródłowy KAŻDEGO pliku nagłówkowego do pliku, który go zawierał. Kompilator kompiluje co .cpp plik. Wynikiem jest liczba plików .obj.
Potem przychodzi łącznik. Linker pobiera wszystkie pliki .obj, zaczynając od głównego pliku, gdy znajdzie odniesienie, które nie ma definicji (np. zmiennej, funkcji lub klasy), próbuje zlokalizować odpowiednią definicję w innych plikach .obj utworzonych na etapie kompilacji lub dostarczonych do linkera na początku etapu linkowania.
Teraz odpowiedź na twoje pytanie: każdy plik .cpp jest kompilowany do pliku .obj zawierającego instrukcje w kodzie maszynowym. Gdy dodajesz plik .hpp i używasz funkcji zdefiniowanej w innym pliku .cpp, Na etapie łączenia linker szuka definicji tej funkcji w odpowiednim pliku .obj. Tak to znajduje.

 1
Author: atoMerz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 08:17:36