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ęzykowe Zobacz 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
 1257
Author: Faisal Vali,
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.
 256
Author: UncaAlby,
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.

 177
Author: sud03r,
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 i eg 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 przez gcc
  • C++ from C: tell g++ to generate unmangled symbols for gcc 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ł.

Przykład na GitHub .

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źć.

Przykład na GitHub .

 131
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
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 includes 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);

 25
Author: tfmontague,
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.

 24
Author: Employed Russian,
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++.

 22
Author: Sander Mertens,
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.

 17
Author: Mark Rushakoff,
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.

 12
Author: Flami,
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);
}
 5
Author: SturmCoder,
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)
{
    //
    //
}
 4
Author: Yogeesh H T,
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++.

 1
Author: Trombe,
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.

 1
Author: Manohar Reddy Poreddy,
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