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
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
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.
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).
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:
- 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.
- 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.
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.
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.
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.
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