Po co nam extern"C" {#include} W C++? [duplikat]

To pytanie ma już odpowiedź tutaj:

Dlaczego musimy używać:

extern "C" {
#include <foo.h>
}

Konkretnie:

  • Kiedy powinniśmy go użyć?

  • Co dzieje się na poziomie kompilatora/linkera, który wymaga od nas jego użycia?

  • Jak w kategoriach kompilacji / linkowania czy to rozwiązuje problemy, które wymagają od nas jej użycia?

Author: Jonathan Leffler, 2008-09-16

10 answers

C i C++ są powierzchownie podobne, ale każdy kompiluje się w zupełnie inny zestaw kodu. Gdy dołączasz plik nagłówkowy do kompilatora C++, kompilator oczekuje kodu C++. Jeśli jednak jest to nagłówek C, kompilator oczekuje, że dane zawarte w pliku nagłówkowym zostaną skompilowane do określonego formatu-C++ 'ABI', lub 'Application Binary Interface' , więc linker się dusi. Jest to lepsze od przekazywania danych C++ do funkcji oczekującej danych C.

(aby dostać się do naprawdę w C++, ABI Zwykle 'manipuluje' nazwami swoich funkcji/metod, więc wywołanie printf() bez oznaczania prototypu jako funkcji C, C++ wygeneruje wywołanie kodu _Zprintf, plus dodatkowe bzdury na końcu.)

Tak więc: użyj extern "C" {...};, gdy dodasz nagłówek c-to takie proste. W przeciwnym razie będziesz miał niedopasowanie w skompilowanym kodzie, a linker się udusi. Jednak w przypadku większości nagłówków nie będziesz nawet potrzebował extern, ponieważ większość nagłówków systemu c już uwzględnia fakt że mogą być zawarte w kodzie C++ i już extern ich kod.

 109
Author: duane,
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
2015-10-15 00:20:08

Extern " C " określa, jak symbole w wygenerowanym pliku obiektowym mają być nazwane. Jeśli funkcja jest zadeklarowana bez extern "C", Nazwa symbolu w pliku obiektowym będzie używać C++ name mangling. Oto przykład.

Podany test.C jak tak:

void foo() { }

Kompilacja i lista symboli w pliku obiektowym daje:

$ g++ -c test.C
$ nm test.o
0000000000000000 T _Z3foov
                 U __gxx_personality_v0

Funkcja foo w rzeczywistości nazywa się "_Z3foov". Łańcuch ten zawiera między innymi informacje o typie zwracanego typu i parametrach. Jeśli zamiast tego napisz test.C jak to:

extern "C" {
    void foo() { }
}

Następnie skompiluj i spójrz na symbole:

$ g++ -c test.C
$ nm test.o
                 U __gxx_personality_v0
0000000000000000 T foo

Dostajesz połączenie C. Nazwa funkcji " foo "w pliku obiektowym to po prostu" foo " i nie ma ona wszystkich fantazyjnych informacji o typie, które pochodzą z namaglowania nazwy.

Zazwyczaj dołączasz nagłówek w extern " C " {} jeśli kod, który się z nim wiąże, został skompilowany za pomocą kompilatora C, ale próbujesz go wywołać z C++. Kiedy to robisz, mówisz kompilatorowi, że wszystkie deklaracje w nagłówku użyje linkowania C. Kiedy połączysz swój kod, Twój .o pliki będą zawierać odniesienia do "foo", a nie" _Z3fooblah", które, miejmy nadzieję, pasują do tego, co jest w bibliotece, z którą linkujesz.

Większość nowoczesnych bibliotek będzie umieszczać osłony wokół takich nagłówków, tak aby symbole były deklarowane z odpowiednim połączeniem. np. w wielu standardowych nagłówkach znajdziesz:

#ifdef __cplusplus
extern "C" {
#endif

... declarations ...

#ifdef __cplusplus
}
#endif

To sprawia, że gdy kod C++ zawiera nagłówek, symbole w pliku obiektowym pasują do tego, co jest w biblioteka C. Powinieneś tylko umieścić extern " C " {} wokół nagłówka C, jeśli jest stary i nie ma już tych strażników.

 104
Author: tgamblin,
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
2008-09-16 00:07:06

W C++ możesz mieć różne encje, które mają wspólną nazwę. Na przykład poniżej znajduje się lista funkcji o nazwie foo :

  • A::foo()
  • B::foo()
  • C::foo(int)
  • C::foo(std::string)

W celu odróżnienia ich wszystkich, kompilator C++ utworzy unikalne nazwy dla każdego z nich w procesie zwanym namaszczaniem nazw lub dekorowaniem. Kompilatory C tego nie robią. Co więcej, każdy kompilator C++ może to zrobić w inny sposób.

Extern" C " mówi C++ kompilator nie wykonuje żadnych namagnesowań nazw na kodzie w nawiasach klamrowych. Pozwala to na wywołanie funkcji C z poziomu C++.

 20
Author: Trent,
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
2017-10-05 07:35:46

Ma to związek ze sposobem, w jaki różne Kompilatory wykonują manipulowanie nazwami. Kompilator C++ zmieni nazwę symbolu wyeksportowanego z pliku nagłówkowego w zupełnie inny sposób niż kompilator C, więc gdy spróbujesz połączyć, pojawi się błąd linkera mówiący o brakujących symbolach.

Aby rozwiązać ten problem, mówimy kompilatorowi C++, aby działał w trybie "C", więc wykonuje on manipulowanie nazw w taki sam sposób, jak kompilator C. Po wykonaniu tego zadania Naprawiono błędy linkera.

 14
Author: 1800 INFORMATION,
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
2008-09-15 23:24:32

Kiedy powinniśmy go użyć?

Podczas łączenia bibliotek C w pliki obiektowe C++

Co się dzieje w poziom kompilatora / linkera, który wymaga od nas żeby go użyć?

C i C++ używają różnych schematów nazewnictwa symboli. To mówi linkerowi, aby używał schematu C podczas linkowania w danej bibliotece.

Jak pod względem kompilacji / linkowania czy to rozwiązuje problemy, które chcesz, żebyśmy go użyli?

Używanie C schemat nazewnictwa pozwala odwoływać się do symboli w stylu C. W przeciwnym razie linker spróbowałby użyć symboli w stylu C++, które nie zadziałałyby.

 11
Author: Tony 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
2008-09-15 23:29:41

C i C++ mają różne zasady dotyczące nazw symboli. Dzięki symbolom linker wie, że wywołanie funkcji " openBankAccount "w jednym pliku obiektowym wyprodukowanym przez kompilator jest odniesieniem do funkcji, którą nazwałeś" openBankAccount " w innym pliku obiektowym wyprodukowanym z innego pliku źródłowego przez ten sam (lub kompatybilny) kompilator. Pozwala to na wykonanie programu z więcej niż jednego pliku źródłowego, co jest ulgą podczas pracy nad dużym projektem.

W C reguła jest bardzo proste, symbole i tak są w jednej przestrzeni nazw. Tak więc liczba całkowita "socks" jest przechowywana jako "socks", a funkcja count_socks jest przechowywana jako "count_socks".

Linkery zostały zbudowane dla języka C i innych języków, takich jak C, z tą prostą regułą nazewnictwa symboli. Tak więc symbole w linkerze są tylko prostymi łańcuchami.

Ale w C++ język pozwala mieć przestrzenie nazw, polimorfizm i różne inne rzeczy, które są sprzeczne z tak prostą regułą. Wszystkie sześć funkcji polimorficznych o nazwie " dodaj" trzeba mieć różne symbole, albo niewłaściwy będzie używany przez inne pliki obiektowe. Odbywa się to poprzez "zniekształcanie" (to termin techniczny) nazw symboli.

Podczas łączenia kodu C++ z bibliotekami lub kodem C, potrzebujesz extern" C " cokolwiek napisanego w C, takie jak pliki nagłówkowe dla bibliotek C, aby powiedzieć kompilatorowi C++, że te nazwy symboli nie mogą być zniekształcone, podczas gdy reszta kodu C++ Oczywiście musi być zniekształcona lub nie będzie działać.

 10
Author: tialaramex,
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
2008-09-15 23:28:54

Powinieneś używać extern " C " za każdym razem, gdy dołączasz nagłówek definiujący funkcje znajdujące się w pliku skompilowanym przez kompilator C, używanym w pliku C++. (Wiele standardowych bibliotek C może zawierać tę kontrolę w swoich nagłówkach, aby ułatwić programistom)

Na przykład, jeśli masz projekt z 3 plikami, util.c, util.h I main.cpp i obie .c i .pliki cpp są kompilowane za pomocą kompilatora C++ (g++, cc, itp.), a następnie nie jest to naprawdę potrzebne, a nawet może powodować błędy linkera. Jeśli proces budowania wykorzystuje zwykły kompilator C dla util.c, wtedy będziesz musiał użyć extern "C", gdy włączysz util.h.

Dzieje się tak, że C++ koduje parametry funkcji w jej nazwie. Tak działa przeciążenie funkcji. Wszystko, co zwykle zdarza się funkcji C, to dodanie podkreślenia ("_") na początku nazwy. Bez użycia extern " C " linker będzie szukał funkcji o nazwie DoSomething@ @ int@float (), gdy rzeczywista nazwa funkcji to _dosomething () lub po prostu Dosometh ().

Użycie extern " C " rozwiązuje powyższy problem, mówiąc kompilatorowi C++, że powinien szukać funkcji zgodnej z konwencją nazewnictwa C zamiast C++.

 7
Author: HitScan,
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
2008-09-15 23:45:40

Konstrukt extern "C" {} nakazuje kompilatorowi nie wykonywać manglowania na nazwach zadeklarowanych w nawiasach klamrowych. Zwykle kompilator C++ "rozszerza" nazwy funkcji tak, że koduje informacje o argumentach i zwracanej wartości; nazywa się to zniekształconą nazwą. Konstrukcja extern "C" zapobiega maglowaniu.

Jest zwykle używany, gdy kod C++ musi wywołać bibliotekę języka C. Może być również używany podczas wystawiania funkcji C++ (na przykład z DLL) Na C klientów.

 6
Author: Paul Lalonde,
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
2008-09-15 23:30:25

Kompilator C++ tworzy nazwy symboli inaczej niż kompilator C. Tak więc, jeśli próbujesz wywołać funkcję znajdującą się w pliku C, skompilowanym jako kod C, musisz powiedzieć kompilatorowi C++, że nazwy symboli, które próbuje rozwiązać, wyglądają inaczej niż domyślnie; w przeciwnym razie krok łącza się nie powiedzie.

 6
Author: mbyrne215,
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
2008-09-16 02:07:52

Służy do rozwiązywania problemów z manipulowaniem nazwami. extern C oznacza, że funkcje są w "płaskim" API w stylu C.

 5
Author: Eric Z Beard,
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
2008-09-15 23:25:09