Jaki jest efekt extern " C " w C++?
Do czego dokładnie służy umieszczenie extern "C"
w kodzie C++?
Na przykład:
extern "C" {
void foo();
}
13 answers
Extern" C " sprawia, że nazwa funkcji w C++ ma połączenie "C" (kompilator nie mangle nazwę) tak, że klient kod C może połączyć się (tzn. użyć) funkcji za pomocą pliku nagłówkowego zgodnego z "C", który zawiera tylko deklarację funkcji. Twoja definicja funkcji jest zawarta w formacie binarnym (który został skompilowany przez kompilator C++), który klient 'C' linker będzie następnie linkował do używając nazwy 'C'.
Ponieważ C++ ma przeciążenie nazw funkcji, A C nie, kompilator C++ nie można po prostu użyć nazwy funkcji jako unikalnego identyfikatora, aby połączyć się z nim, więc zmienia nazwę poprzez dodanie informacji o argumentach. Kompilator C nie musi manipulować nazwą, ponieważ nie można przeciążać nazw funkcji w C. gdy stwierdzisz, że funkcja ma zewnętrzne powiązanie " C " W C++, kompilator C++ nie dodaje informacji o typie argumentu/parametru do nazwy używanej do łączenia.
Tak dla twojej wiadomości, możesz określić" C " powiązanie z każdą indywidualną deklaracją/definicją jawnie lub użyć blok do grupowania sekwencji deklaracji/definicji o pewnym powiązaniu:
extern "C" void foo(int);
extern "C"
{
void g(char);
int i;
}
Jeśli zależy ci na szczegółach technicznych, są one wymienione w sekcji 7.5 standardu C++03, oto krótkie podsumowanie (z naciskiem na extern "C"):
- extern " C " is a linkage-specification
- każdy kompilator jest wymagany do zapewnienia linkowania " C "
- Specyfikacja linkowania występuje tylko w obszarze przestrzeni nazw
-
wszystkie typy funkcji, funkcja nazwy i nazwy zmiennych mają powiązania językoweZobacz komentarz Ryszarda: tylko nazwy funkcji i nazw zmiennych z powiązaniem zewnętrznym mają powiązanie językowe - dwa typy funkcji z odrębnymi powiązaniami językowymi są odrębnymi typami, nawet jeśli w przeciwnym razie są identyczne
- Gniazdo specyfikacji połączeń, wewnętrzne określa ostateczne połączenie
- extern " C " jest ignorowany dla członków klasy
- co najwyżej jedna funkcja o określonej nazwie może mieć łącze "C" (niezależnie od przestrzeni nazw)
-
extern " C " wymusza, aby funkcja miała połączenie zewnętrzne (nie może być statyczna)Zobacz komentarz Richarda: 'static' wewnątrz 'extern" C "' jest poprawny; byt tak zadeklarowany ma wewnętrzne powiązanie, a więc nie ma powiązania językowego - powiązanie z C++ do obiektów zdefiniowanych w innych językach i do obiektów zdefiniowanych w C++ z innych języków jest zdefiniowane implementacyjnie i zależne od języka. Tylko wtedy, gdy układ obiektu strategie dwóch implementacji językowych są na tyle podobne, że można osiągnąć takie powiązanie
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-05-23 12:02:57
Chciałem tylko dodać trochę informacji, ponieważ nie widziałem go jeszcze opublikowane.
Bardzo często zobaczysz kod w nagłówkach C w następujący sposób:
#ifdef __cplusplus
extern "C" {
#endif
// all of your legacy C code here
#ifdef __cplusplus
}
#endif
Pozwala to na użycie pliku nagłówkowego C z kodem C++, ponieważ zostanie zdefiniowane makro "_ _ cplusplus". Ale możesz również nadal używać go ze swoim starszym kodem C, gdzie makro jest zdefiniowane , a nie, więc nie będzie widzieć unikalnej konstrukcji C++.
Chociaż widziałem też kod C++ taki as:
extern "C" {
#include "legacy_C_header.h"
}
Co, jak sobie wyobrażam, czyni to samo.
Nie wiem, która droga jest lepsza, ale widziałem oba.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-10-21 01:08:32
W każdym programie C++ wszystkie funkcje niestatyczne są reprezentowane w pliku binarnym jako symbole. Symbole te są specjalnymi ciągami tekstowymi, które jednoznacznie identyfikują funkcję w programie.
W C Nazwa symbolu jest taka sama jak nazwa funkcji. Jest to możliwe, ponieważ w C no dwie funkcje niestatyczne mogą mieć tę samą nazwę.
Ponieważ C++ pozwala na przeciążenie i ma wiele funkcji, których C nie lubi - jak klasy, funkcje Członkowskie, specyfikacje WYJĄTKÓW - nie jest to możliwe aby po prostu użyć nazwy funkcji jako nazwy symbolu. Aby to rozwiązać, C++ używa tak zwanego namaglowania nazw, które przekształca nazwę funkcji i wszystkie niezbędne informacje (takie jak liczba i rozmiar argumentów) w jakiś dziwnie wyglądający ciąg przetwarzany tylko przez kompilator i linker.
Więc jeśli określisz funkcję, która ma być extern C, kompilator nie wykona z nią manipulowania nazwami i może być bezpośrednio dostęp za pomocą nazwy symbolu jako nazwy funkcji.
This comes przydatne podczas używania dlsym()
i dlopen()
do wywoływania takich 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
2018-03-03 15:48:25
Dekompilacja g++
wygenerowanego pliku binarnego, aby zobaczyć, co się dzieje
Wejście:
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Skompiluj z wyjściem GCC 4.8 Linux ELF:
g++ -c a.cpp
Dekompilacja tabeli symboli:
readelf -s a.o
Wyjście zawiera:
Num: Value Size Type Bind Vis Ndx Name
8: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 _Z1fv
9: 0000000000000006 6 FUNC GLOBAL DEFAULT 1 ef
10: 000000000000000c 16 FUNC GLOBAL DEFAULT 1 _Z1hv
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg
Interpretacja
Widzimy, że:
-
ef
ieg
były przechowywane w symbolach o tej samej nazwie, co w kodzie -
Pozostałe symbole zostały zniekształcone. Rozłączmy się. oni:
$ c++filt _Z1fv f() $ c++filt _Z1hv h() $ c++filt _Z1gv g()
Wniosek: oba poniższe typy symboli były Nie zniekształcone:
- zdefiniowane [19]}), które należy podać przy łączu lub w czasie uruchomienia z innego pliku obiektowego
Więc będziesz potrzebował extern "C"
zarówno podczas wywołania:
- C Z C++: powiedz
g++
, aby oczekiwać niezmieszanych symboli wyprodukowanych przezgcc
- C++ from C: tell
g++
to generate unmangled symbols forgcc
to użycie
Rzeczy, które nie działają w extern C
Staje się oczywiste, że każda funkcja C++, która wymaga namaszczania nazw, nie będzie wok wewnątrz extern C
:
extern "C" {
// Overloading.
// error: declaration of C function ‘void f(int)’ conflicts with
void f();
void f(int i);
// Templates.
// error: template with C linkage
template <class C> void f(C i) { }
}
Minimal runnable C from C++ example
Ze względu na kompletność i dla nowych ludzi.
Wywołanie C z C++ jest dość proste: każda funkcja C ma tylko jeden możliwy symbol, więc nie ma dodatkowej pracy wymagane.
Main.cpp:
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
C. h:
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
C. c:
#include "c.h"
int f(void) { return 1; }
Run:
g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out
BEZ extern "C"
link nie działa z:
main.cpp:6: undefined reference to `f()'
Ponieważ g++
spodziewa się znaleźć zniekształcony f
, który gcc
nie wyprodukował.
Minimal runnable C++ from C example
Wywołanie C++ from jest nieco trudniejsze: musimy ręcznie tworzyć nie zniekształcone wersje każdej funkcji, którą chcemy expose.
Tutaj zilustrujemy, jak ujawnić przeciążenia funkcji C++ do C.
Main.c:
#include <assert.h>
#include "cpp.h"
int main(void) {
assert(f_int(1) == 2);
assert(f_float(1.0) == 3);
return 0;
}
Cpp.h:
#ifndef CPP_H
#define CPP_H
#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif
#endif
Cpp.cpp:
#include "cpp.h"
int f(int i) {
return i + 1;
}
int f(float i) {
return i + 2;
}
int f_int(int i) {
return f(i);
}
int f_float(float i) {
return f(i);
}
Run:
gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out
BEZ extern "C"
zawodzi z:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
Ponieważ g++
wygenerowano zniekształcone symbole, których gcc
nie można znaleźć.
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
2018-09-26 16:35:13
C++ modyfikuje nazwy funkcji, aby utworzyć obiektowy język z języka proceduralnego
Większość języków programowania nie jest zbudowana na bazie istniejących języków programowania. C++ jest oparty na C, a ponadto jest to obiektowy język programowania zbudowany z proceduralnego języka programowania, z tego powodu istnieją słowa kluczowe C++, takie jak extern
, które zapewniają wsteczną kompatybilność z C[12]}
Spójrzmy na następujący przykład:
#include <stdio.h>
// Two functions are defined with the same name
// but have different parameters
void printMe(int a) {
printf("int: %i\n", a);
}
void printMe(char a) {
printf("char: %c\n", a);
}
int main() {
printMe("a");
printMe(1);
return 0;
}
A Kompilator C nie skompiluje powyższego przykładu, ponieważ ta sama funkcja printMe
jest zdefiniowana dwukrotnie(nawet jeśli mają różne parametry int a
vs char a
).
Gcc - o printMe printMe.c && ./ printMe;
1 błąd. PrintMe jest zdefiniowany więcej niż raz.
Kompilator C++ skompiluje powyższy przykład. Nie obchodzi mnie, że printMe
jest zdefiniowane dwukrotnie.
G++ - o printMe printMe.c && ./ printMe;
To dlatego, że C++ kompilator domyślnie zmienia nazwy (mangles ) funkcji na podstawie ich parametrów. W języku C Ta funkcja nie była obsługiwana. Jednak, gdy C++ został zbudowany nad C, język został zaprojektowany tak, aby był zorientowany obiektowo i musiał wspierać zdolność do tworzenia różnych klas z metodami (funkcjami) o tej samej nazwie oraz do nadpisywania metod (metoda nadpisująca) w oparciu o różne parametry.
Extern mówi "nie wymawiaj nazw funkcji"
Wyobraźmy sobie jednak, że mamy starszy plik C o nazwie " parent.c "że include
s nazwy funkcji z innych starszych plików C," rodzic.H", " dziecko.h", itp. Jeśli dziedzictwo " rodzic.plik c "jest uruchamiany przez kompilator C++, wtedy nazwy funkcji zostaną zniekształcone i nie będą już pasować do nazw funkcji określonych w" rodzicu.H", " dziecko.h", itd. - więc nazwy funkcji w tych zewnętrznych plikach również musiałyby być zniekształcone. Zniekształcanie nazw funkcji w złożonym programie C, tych z wieloma zależnościami, może prowadzić do złamania kodu; więc to może być wygodne podanie słowa kluczowego, które może powiedzieć kompilatorowi C++, aby nie zmieniał nazwy funkcji.
Słowo kluczowe extern
mówi kompilatorowi C++, aby nie zmieniał nazw funkcji. Przykład użycia: extern void printMe(int a);
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
2018-09-28 19:44:06
Zmienia powiązanie funkcji w taki sposób, że funkcję można wywołać z C. w praktyce oznacza to, że nazwa funkcji nie jest zniekształcona.
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
2009-06-25 02:12:21
Żaden nagłówek C nie będzie kompilowany z extern "C". Gdy identyfikatory w nagłówku C konflikt ze słowami kluczowymi C++ kompilator C++ będzie narzekać na to.
Na przykład, widziałem następujący kod fail w g++ :
extern "C" {
struct method {
int virtual;
};
}
To ma sens, ale warto o tym pamiętać przy przenoszeniu kodu C do c++.
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-01-26 22:22:21
Informuje kompilator C++, aby podczas łączenia szukał nazw tych funkcji w stylu C, ponieważ nazwy funkcji skompilowanych w C i C++ są różne w etapie łą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
2009-06-25 02:12:12
Extern "C"ma być rozpoznane przez kompilator C++ i powiadamiać kompilatora, że notowana funkcja jest (lub ma być) skompilowana w stylu C. Tak, że podczas linkowania łączy się z poprawną wersją funkcji z C.
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-04-10 09:46:10
Używałem wcześniej 'extern" C "' dla plików dll (dynamic link library) do tworzenia itp. main () funkcja "exportable", więc może być użyta później w innym pliku wykonywalnym z biblioteki dll. Może przydałby się przykład, gdzie go używałem.
DLL
#include <string.h>
#include <windows.h>
using namespace std;
#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}
EXE
#include <string.h>
#include <windows.h>
using namespace std;
typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder
int main()
{
char winDir[MAX_PATH];//will hold path of above dll
GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
strcat(winDir,"\\exmple.dll");//concentrate dll name with path
HINSTANCE DLL = LoadLibrary(winDir);//load example dll
if(DLL==NULL)
{
FreeLibrary((HMODULE)DLL);//if load fails exit
return 0;
}
mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
//defined variable is used to assign a function from dll
//GetProcAddress is used to locate function with pre defined extern name "DLL"
//and matcing function name
if(mainDLLFunc==NULL)
{
FreeLibrary((HMODULE)DLL);//if it fails exit
return 0;
}
mainDLLFunc();//run exported function
FreeLibrary((HMODULE)DLL);
}
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
2014-07-01 15:43:34
extern "C"
jest specyfikacją połączenia, która jest używana do wywołania funkcji C w plikach źródłowych Cpp . Możemy wywoływać funkcje C, zapisywać zmienne i dołączać nagłówki . Funkcja jest zadeklarowana w encji extern i jest zdefiniowana na zewnątrz. Składnia to
Typ 1:
extern "language" function-prototype
Typ 2:
extern "language"
{
function-prototype
};
Eg:
#include<iostream>
using namespace std;
extern "C"
{
#include<stdio.h> // Include C Header
int n; // Declare a Variable
void func(int,int); // Declare a function (function prototype)
}
int main()
{
func(int a, int b); // Calling function . . .
return 0;
}
// Function definition . . .
void func(int m, int n)
{
//
//
}
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-11-17 12:09:03
Podczas mieszania C i C++ (tj. a. wywołanie funkcji C z C++; i b. wywołanie funkcji c++ z C), Nazwa C++ mangling powoduje problemy z połączeniem. Technicznie rzecz biorąc, problem ten występuje tylko wtedy, gdy funkcje callee zostały już skompilowane do postaci binarnej (najprawdopodobniej a *.pliku bibliotecznego) przy użyciu odpowiedniego kompilatora.
Więc musimy użyć extern "C", aby wyłączyć nazwę mangling w C++.
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-08-07 07:54:12
Ta odpowiedź jest dla niecierpliwych / mają terminy do spełnienia, tylko część/proste wyjaśnienie jest poniżej:
- W C++, możesz mieć tę samą nazwę w klasie przez przeciążenie (na przykład, ponieważ wszystkie są takie same nazwy nie mogą być eksportowane jako-jest z dll, itd.) rozwiązanie tych problemów polega na tym, że są one konwertowane na różne ciągi znaków (zwane symbolami), symbole zawierają nazwę funkcji, również argumenty, więc każda z tych funkcji nawet o tej samej nazwie może być jednoznacznie zidentyfikowana (także called, name mangling)
- w C, nie ma przeciążenia, nazwa funkcji jest unikalna (więc oddzielny ciąg znaków do jednoznacznej identyfikacji nazwy funkcji a nie jest wymagany, więc symbolem jest sama nazwa funkcji)
Więc
w C++, z nazwą każdej funkcji
w C, nawet bez nazwy, każda funkcja
Aby zmienić zachowanie C++, czyli podać nazwę nie powinno się szczególna Funkcja, możesz użyć extern"C" przed nazwą funkcji, z jakiegokolwiek powodu, jak eksport funkcji o określonej nazwie z biblioteki dll, do użytku przez jej klientów.
Przeczytaj inne odpowiedzi, aby uzyskać bardziej szczegółowe/poprawne odpowiedzi.
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
2018-07-24 13:41:42