Qual è l'effetto di "C" esterno in C ++?


1632

Cosa fa esattamente l'inserimento extern "C"nel codice C ++?

Per esempio:

extern "C" {
   void foo();
}

83
Vorrei presentarti questo articolo: http://www.agner.org/optimize/calling_conventions.pdf Ti dice molto di più su come chiamare le convenzioni e la differenza tra i compilatori.
Sam Liao,

1
@Litherum In cima alla mia testa, sta dicendo al compilatore di compilare quell'ambito di codice usando C, dato che hai un cross-compilatore. Inoltre, significa che hai un file Cpp in cui hai quella foo()funzione.
ha9u63ar,

1
@ ha9u63ar È "fuori dalla mia testa". Anche il resto del tuo commento è errato. Ti consiglio di cancellarlo.
TamaMcGlinn

Risposte:


1560

extern "C" fa in modo che un nome-funzione in C ++ abbia un collegamento 'C' (il compilatore non altera il nome) in modo che il codice client C possa collegarsi (cioè usare) la tua funzione usando un file di intestazione compatibile 'C' che contiene solo il dichiarazione della tua funzione. La definizione della funzione è contenuta in un formato binario (compilato dal compilatore C ++) a cui il linker "C" del client collegherà quindi utilizzando il nome "C".

Poiché C ++ ha un sovraccarico di nomi di funzioni e C no, il compilatore C ++ non può semplicemente utilizzare il nome della funzione come ID univoco a cui collegarsi, quindi altera il nome aggiungendo informazioni sugli argomenti. Il compilatore CA non deve modificare il nome poiché non è possibile sovraccaricare i nomi delle funzioni in C. Quando si afferma che una funzione ha un collegamento "C" esterno in C ++, il compilatore C ++ non aggiunge informazioni sull'argomento / tipo di parametro al nome utilizzato per linkage.

Solo per questo, puoi specificare il collegamento "C" a ogni singola dichiarazione / definizione in modo esplicito o utilizzare un blocco per raggruppare una sequenza di dichiarazioni / definizioni in modo da avere un certo collegamento:

extern "C" void foo(int);
extern "C"
{
   void g(char);
   int i;
}

Se ti interessano i tecnicismi, sono elencati nella sezione 7.5 della norma C ++ 03, ecco un breve riassunto (con enfasi sull'esterno "C"):

  • extern "C" è una specifica di collegamento
  • Ogni compilatore deve fornire il collegamento "C"
  • una specifica di collegamento deve essere presente solo nell'ambito dello spazio dei nomi
  • tutti i tipi di funzioni, i nomi delle funzioni e i nomi delle variabili hanno un collegamento linguistico Vedi il commento di Richard: Solo i nomi delle funzioni e i nomi delle variabili con collegamento esterno hanno un collegamento linguistico
  • due tipi di funzioni con collegamenti linguistici distinti sono tipi distinti anche se altrimenti identici
  • annidamento delle specifiche del collegamento, quello interno determina il collegamento finale
  • extern "C" viene ignorato per i membri della classe
  • al massimo una funzione con un nome particolare può avere un collegamento "C" (indipendentemente dallo spazio dei nomi)
  • extern "C" forza una funzione per avere un collegamento esterno (non può renderla statica) Vedi il commento di Richard: 'statico' dentro 'extern "C"' è valido; un'entità così dichiarata ha un collegamento interno e quindi non ha un collegamento linguistico
  • Il collegamento dal C ++ agli oggetti definiti in altre lingue e agli oggetti definiti in C ++ da altre lingue è definito dall'implementazione e dipendente dalla lingua. Solo laddove le strategie di layout degli oggetti di due implementazioni linguistiche sono abbastanza simili è possibile ottenere tale collegamento

22
Il compilatore C non usa il mangling come fa il c ++. Quindi, se si desidera chiamare l'interfaccia ca da un programma c ++, è necessario dichiarare chiaramente che l'interfaccia c come "extern c".
Sam Liao,

59
@Faisal: non tentare di collegare il codice creato con compilatori C ++ diversi, anche se i riferimenti incrociati sono tutti "C" esterni. Esistono spesso differenze tra i layout delle classi o i meccanismi utilizzati per gestire le eccezioni, oppure i meccanismi utilizzati per garantire l'inizializzazione delle variabili prima dell'uso, o altre differenze simili, inoltre potrebbero essere necessarie due librerie di supporto runtime C ++ separate (una per ogni compilatore).
Jonathan Leffler,

8
"extern" C "impone a una funzione di avere un collegamento esterno (non può renderlo statico)" non è corretto. 'statico' dentro 'extern "C"' è valido; un'entità così dichiarata ha un collegamento interno e quindi non ha un collegamento linguistico.
Richard Smith,

14
'tutti i tipi di funzioni, nomi di funzioni e nomi di variabili hanno un collegamento linguistico' non è corretto. Solo i nomi delle funzioni e i nomi delle variabili con collegamento esterno hanno un collegamento linguistico.
Richard Smith,

9
Nota che extern "C" { int i; }è una definizione. Questo potrebbe non essere quello che volevi, accanto alla non definizione di void g(char);. Per renderlo una non-definizione, avresti bisogno extern "C" { extern int i; }. D'altra parte, la sintassi di una dichiarazione senza parentesi graffe rende la dichiarazione una non-definizione: extern "C" int i;è la stessa diextern "C" { extern int i; }
aschepler

327

Volevo solo aggiungere un po 'di informazioni, dal momento che non l'ho ancora visto pubblicato.

Vedrai molto spesso il codice nelle intestazioni C in questo modo:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Ciò che ottiene è che ti consente di utilizzare quel file di intestazione C con il tuo codice C ++, perché verrà definita la macro "__cplusplus". Ma puoi anche usarlo con il tuo codice C legacy, dove la macro NON è definita, quindi non vedrà il costrutto C ++ in modo univoco.

Anche se ho visto anche codice C ++ come:

extern "C" {
#include "legacy_C_header.h"
}

che immagino realizza più o meno la stessa cosa.

Non sono sicuro di quale sia la strada migliore, ma ho visto entrambi.


11
C'è una netta differenza. Nel primo caso, se si compila questo file con un normale compilatore gcc, verrà generato un oggetto in cui il nome della funzione non è alterato. Se quindi si collegano oggetti C e C ++ con il linker NON troveranno le funzioni. Dovrai includere quei file "legacy header" con la parola chiave extern come nel tuo secondo blocco di codice.
Anne van Rossum,

8
@Anne: il compilatore C ++ cercherà anche nomi non confusi, perché è stato visto extern "C"nell'intestazione). Funziona alla grande, ha usato questa tecnica molte volte.
Ben Voigt,

20
@Anne: Non è vero, anche il primo va bene. È ignorato dal compilatore C e ha lo stesso effetto del secondo in C ++. Al compilatore non potrebbe importare di meno se incontra extern "C"prima o dopo include l'intestazione. Quando raggiunge il compilatore, è comunque solo un lungo flusso di testo preelaborato.
Ben Voigt,

8
@Anne, no, penso che tu sia stato influenzato da qualche altro errore nella fonte, perché quello che stai descrivendo è sbagliato. Nessuna versione di g++questo ha sbagliato, per nessun obiettivo, almeno negli ultimi 17 anni. L'intero punto del primo esempio è che non importa se si utilizza un compilatore C o C ++, non verrà eseguita alcuna modifica del nome per i nomi nel extern "C"blocco.
Jonathan Wakely, il

7
"quale è meglio" - sicuramente la prima variante è migliore: consente di includere direttamente l'intestazione, senza ulteriori requisiti, sia in codice C che C ++. Il secondo approccio è una soluzione alternativa per le intestazioni C, l'autore ha dimenticato le protezioni C ++ (nessun problema, tuttavia, se queste vengono aggiunte in seguito, vengono accettate dichiarazioni "C" esterne nidificate ...).
Aconcagua,

267

Decompila un g++binario generato per vedere cosa sta succedendo

main.cpp

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

Compilare e disassemblare l' output ELF generato :

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

L'output contiene:

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

Interpretazione

Lo vediamo:

  • efe egsono stati memorizzati in simboli con lo stesso nome del codice

  • gli altri simboli erano mutilati. Districiamoli:

    $ c++filt _Z1fv
    f()
    $ c++filt _Z1hv
    h()
    $ c++filt _Z1gv
    g()

Conclusione: entrambi i seguenti tipi di simboli non sono stati alterati:

  • definito
  • dichiarato ma non definito ( Ndx = UND), da fornire al collegamento o in fase di esecuzione da un altro file oggetto

Quindi avrai bisogno di extern "C"entrambi quando chiami:

  • C da C ++: dire g++di aspettarsi simboli non confusi prodotti dagcc
  • C ++ da C: g++indica di generare simboli non confusi gccda usare

Cose che non funzionano nell'esterno C

Diventa ovvio che qualsiasi funzione C ++ che richiede la modifica del nome non funzionerà all'interno 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) { }
}

Eseguibile minimo C dall'esempio C ++

Per completezza e per i neofiti là fuori, vedi anche: Come usare i file sorgente C in un progetto C ++?

Chiamare C da C ++ è abbastanza semplice: ogni funzione C ha un solo simbolo non distorto, quindi non è necessario alcun lavoro extra.

main.cpp

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

ch

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

cc

#include "c.h"

int f(void) { return 1; }

Correre:

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

Senza extern "C"il collegamento fallisce con:

main.cpp:6: undefined reference to `f()'

perché si g++aspetta di trovare un uomo mutilato f, che gccnon ha prodotto.

Esempio su GitHub .

Esempio C ++ eseguibile minimo da C.

Chiamare C ++ da C è un po 'più difficile: dobbiamo creare manualmente versioni non alterate di ogni funzione che vogliamo esporre.

Qui illustriamo come esporre i sovraccarichi della funzione C ++ a 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);
}

Correre:

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

Senza di extern "C"essa fallisce con:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

perché ha g++generato simboli alterati che gccnon è possibile trovare.

Esempio su GitHub .

Testato su Ubuntu 18.04.


21
La migliore risposta da quando hai 1) menzione esplicita che extern "C" {ti aiuta a chiamare funzioni C non combinate dall'interno dei programmi C ++ , così come le funzioni C ++ non distrutte dall'interno dei programmi C , che altre risposte non rendono così ovvio, e 2) perché mostri esempi distinti di ogni. Grazie!
Gabriel Staples,

3
Mi piace molto questa risposta
avvio automatico

4
Giù le mani la risposta migliore poiché mostra come chiamare le funzioni sovraccariche da c
Gaspa79

1
@JaveneCPPMcGowan cosa ti fa pensare di avere un insegnante di C ++? :-)
Ciro Santilli 1 冠状 病 六四 事件 法轮功

205

In ogni programma C ++, tutte le funzioni non statiche sono rappresentate nel file binario come simboli. Questi simboli sono stringhe di testo speciali che identificano in modo univoco una funzione nel programma.

In C, il nome del simbolo è uguale al nome della funzione. Ciò è possibile perché in C non due funzioni non statiche possono avere lo stesso nome.

Poiché C ++ consente il sovraccarico e ha molte caratteristiche che C non gradisce - come classi, funzioni membro, specifiche di eccezione - non è possibile utilizzare semplicemente il nome della funzione come nome del simbolo. Per risolverlo, C ++ usa il cosiddetto nome mangling, che trasforma il nome della funzione e tutte le informazioni necessarie (come il numero e la dimensione degli argomenti) in una stringa dall'aspetto strano elaborata solo dal compilatore e dal linker.

Pertanto, se si specifica una funzione come extern C, il compilatore non esegue la modifica del nome con essa e vi si può accedere direttamente usando il nome del suo simbolo come nome della funzione.

Questo è utile durante l'utilizzo dlsym()e dlopen()per chiamare tali funzioni.


cosa intendi per pratico? è il nome del simbolo = il nome della funzione renderebbe noto il nome del simbolo passato a dlsym o altro?
Errore

1
@Error: si. È sostanzialmente impossibile nel caso generale dlopen () una libreria condivisa C ++ dato solo un file header e scegliere la funzione giusta da caricare. (Su x86, è stata pubblicata una specifica di modifica del nome pubblicata sotto forma di ABI Itanium che tutti i compilatori x86 che conosco servono per manipolare i nomi delle funzioni C ++, ma nulla nella lingua lo richiede.)
Jonathan Tomer

52

C ++ mangles nomi di funzioni per creare un linguaggio orientato agli oggetti da un linguaggio procedurale

La maggior parte dei linguaggi di programmazione non è costruita sulla base dei linguaggi di programmazione esistenti. C ++ è costruito sopra C, e inoltre è un linguaggio di programmazione orientato agli oggetti costruito da un linguaggio di programmazione procedurale, e per questo motivo ci sono espressioni C ++ come quelle extern "C"che forniscono retrocompatibilità con C.

Diamo un'occhiata al seguente esempio:

#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;
}

Il compilatore CA non compilerà l'esempio sopra, perché la stessa funzione printMeè definita due volte (anche se hanno parametri diversi int arispetto a char a).

gcc -o printMe printMe.c && ./printMe;
1 errore PrintMe è definito più di una volta.

Un compilatore C ++ compilerà l'esempio sopra. Non importa che printMesia definito due volte.

g ++ -o printMe printMe.c && ./printMe;

Questo perché un compilatore C ++ rinomina implicitamente ( mangles ) le funzioni in base ai loro parametri. In C, questa funzione non era supportata. Tuttavia, quando C ++ è stata costruita sopra C, il linguaggio è stato progettato per essere orientato agli oggetti, e aveva bisogno di sostenere la capacità di creare diverse classi con i metodi (funzioni) con lo stesso nome, e per sovrascrivere i metodi ( l'override dei metodi ) basati su differenti parametri.

extern "C" dice "non manipolare i nomi delle funzioni C"

Tuttavia, immagina di avere un file C legacy denominato "parent.c" che contiene includenomi di funzioni da altri file C legacy, "parent.h", "child.h", ecc. Se il file "parent.c" legacy viene eseguito tramite un compilatore C ++, i nomi delle funzioni saranno alterati e non corrisponderanno più ai nomi delle funzioni specificati in "parent.h", "child.h", ecc., quindi i nomi delle funzioni in quei file esterni dovrebbero anche essere distrutto. La modifica dei nomi delle funzioni in un programma C complesso, quelli con molte dipendenze, può portare a un codice errato; quindi potrebbe essere conveniente fornire una parola chiave che indichi al compilatore C ++ di non modificare il nome di una funzione.

La extern "C"parola chiave indica a un compilatore C ++ di non modificare (rinominare) i nomi delle funzioni C.

Per esempio:

extern "C" void printMe(int a);


non possiamo usarlo extern "C"se abbiamo solo un dllfile? Voglio dire se non abbiamo un file header e abbiamo solo un file sorgente (solo implementazioni) e l'uso della sua funzione tramite il puntatore a funzione. in questo stato, abbiamo appena usato delle funzioni (indipendentemente dal suo nome).
BattleTested

@tfmontague, per me l'hai inchiodato nel modo giusto! dritto nella testa.
Artanis Zeratul,

29

Nessun header C può essere reso compatibile con C ++ semplicemente racchiudendo in "C" esterno. Quando gli identificatori in un header C entrano in conflitto con le parole chiave C ++, il compilatore C ++ si lamenterà di questo.

Ad esempio, ho visto il seguente codice fallire in un g ++:

extern "C" {
struct method {
    int virtual;
};
}

Kinda ha un senso, ma è qualcosa da tenere a mente quando si esegue il porting del codice C in C ++.


14
extern "C"significa utilizzare il collegamento C, come descritto da altre risposte. Non significa "compilare i contenuti come C" o altro. int virtual;non è valido in C ++ e la specifica di collegamenti diversi non lo modifica.
MM

1
... o modalità in generale, qualsiasi codice contenente errori di sintassi non verrà compilato.
Valentin Heinitz,

4
@ValentinHeinitz naturalmente, sebbene l'uso di "virtual" come identificatore in C non sia un errore di sintassi. Volevo solo sottolineare che non è possibile utilizzare automaticamente alcuna intestazione C in C ++ inserendo "C" esterno.
Sander Mertens,

28

Cambia il collegamento di una funzione in modo tale che la funzione sia richiamabile da C. In pratica ciò significa che il nome della funzione non è alterato .


3
Mangled è il termine generalmente usato ... Non credo di aver mai visto 'decorato' usato con questo significato.
Matthew Scharley,

1
Microsoft (almeno in parte) usa decorati piuttosto che alterati nella loro documentazione. danno persino un nome al loro strumento per declassare (alias un-mangle) un nome undname.
René Nyffenegger,

20

Informa il compilatore C ++ di cercare i nomi di tali funzioni in stile C durante il collegamento, poiché i nomi delle funzioni compilate in C e C ++ sono diversi durante la fase di collegamento.


12

"C" esterno deve essere riconosciuto da un compilatore C ++ e notificare al compilatore che la nota funzione è (o deve essere) compilata in stile C. In modo che durante il collegamento, si collega alla versione corretta della funzione da C.


6

Ho usato 'extern "C"' prima per i file dll (libreria di collegamento dinamico) per rendere la funzione main () ecc. "Esportabile" in modo che possa essere utilizzata in seguito in un altro eseguibile da dll. Forse un esempio di dove lo usavo può essere utile.

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);
}

4
Bogus. extern "C"e __declspec(dllexport)non sono correlati. Il primo controlla la decorazione del simbolo, il secondo è responsabile della creazione di una voce di esportazione. Puoi anche esportare un simbolo usando anche la decorazione del nome C ++. Oltre a perdere completamente il punto di questa domanda, ci sono anche altri errori nell'esempio di codice. Per uno, mainesportato dalla tua DLL non dichiara un valore di ritorno. O chiamando la convenzione, del resto. Durante l'importazione, si attribuisce una convenzione di chiamata casuale ( WINAPI) e si utilizza il simbolo errato per build a 32 bit (dovrebbe essere _maino _main@0). Scusa, -1.
Indispensabile il

1
Ciò ripetuto, che non sai, che cosa stai facendo, ma farlo in questo modo sembra funzionare per te, per un elenco sconosciuto di piattaforme target. Non hai affrontato i problemi che ho sollevato nel mio commento precedente. Questo è ancora un voto negativo, a causa di un grosso errore (c'è di più, che non rientrava in un singolo commento).
Probabile

1
Pubblicare una risposta su Stack Overflow implica che tu sappia cosa stai facendo. Questo è previsto. Per quanto riguarda il tuo tentativo di "prevenire il danneggiamento dello stack in esecuzione" : la firma della tua funzione specifica un valore di ritorno di tipo void*, ma l'implementazione non restituisce nulla. Volerà davvero bene ...
Probabile

1
Se implementate qualcosa, che sembra funzionare, per pura fortuna, allora chiaramente non sapete cosa state facendo (il vostro campione "funzionante" rientra in quella categoria). È un comportamento indefinito e sembra funzionare è una forma valida di comportamento indefinito. È ancora indefinito. Lo apprezzerei molto se in futuro avessi esercitato maggiore diligenza. Parte di ciò potrebbe essere l'eliminazione di questa risposta proposta.
Probabile

1
Stai reinterpretando una funzione che non restituisce nulla come funzione che restituisce un puntatore. È pura fortuna che x86 sia molto tollerante rispetto alle firme di funzioni non corrispondenti, e in particolare valori di ritorno di tipo integrale. Il tuo codice funziona solo per coincidenza. Se non sei d'accordo, devi spiegare perché il tuo codice funziona in modo affidabile.
Probabile

5

extern "C"è una specifica di collegamento che viene utilizzata per chiamare le funzioni C nei file di origine Cpp . Possiamo chiamare funzioni C, scrivere variabili e includere intestazioni . La funzione è dichiarata nell'entità esterna ed è definita all'esterno. La sintassi è

Tipo 1:

extern "language" function-prototype

Tipo 2:

extern "language"
{
     function-prototype
};

per esempio:

#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)
{
    //
    //
}

3

Questa risposta è per gli impazienti / hanno delle scadenze da rispettare, solo una parte / semplice spiegazione è di seguito:

  • in C ++, è possibile avere lo stesso nome in classe tramite sovraccarico (ad esempio, poiché sono tutti uguali non possono essere esportati così come sono dalla dll, ecc.) la soluzione a questi problemi è che vengono convertiti in stringhe diverse (chiamati simboli ), i simboli spiegano il nome della funzione, anche gli argomenti, quindi ognuna di queste funzioni, anche con lo stesso nome, può essere identificata in modo univoco (anche chiamato, mengling del nome)
  • in C, non hai un sovraccarico, il nome della funzione è univoco (quindi, non è richiesta una stringa separata per identificare un nome di funzione in modo univoco, quindi il simbolo è il nome della funzione stessa)

Quindi
in C ++, con il nome che modifica le identità in modo univoco ogni funzione
in C, anche senza il nome che modifica le identità in modo univoco ogni funzione

Per modificare il comportamento di C ++, ovvero specificare che la modifica del nome non dovrebbe avvenire per una particolare funzione, è possibile utilizzare "C" esterno prima del nome della funzione, per qualsiasi motivo, come esportare una funzione con un nome specifico da una dll , ad uso dei suoi clienti.

Leggi altre risposte, per risposte più dettagliate / più corrette.


1

Quando si mescolano C e C ++ (cioè, chiamando la funzione C da C ++; e b. Chiamando la funzione C ++ da C), la modifica del nome C ++ causa problemi di collegamento. Tecnicamente parlando, questo problema si verifica solo quando le funzioni di chiamata sono già state compilate in binario (molto probabilmente, un file di libreria * .a) usando il compilatore corrispondente.

Quindi abbiamo bisogno di usare la "C" esterna per disabilitare la modifica del nome in C ++.


0

Senza entrare in conflitto con altre buone risposte, aggiungerò un po 'del mio esempio.

Cosa fa esattamente il compilatore C ++ : manipola i nomi nel processo di compilazione, quindi abbiamo bisogno di dire al compilatore di trattare l' C implementazione in modo speciale.

Quando stiamo creando e aggiungendo classi C ++ extern "C", stiamo dicendo al nostro compilatore C ++ che stiamo usando la convenzione di chiamata C.

Motivo (stiamo chiamando l'implementazione C da C ++): o vogliamo chiamare la funzione C da C ++ o chiamare la funzione C ++ da C (classi C ++ ... ecc. Non funzionano in C).


Benvenuto in Stack Overflow. Se decidi di rispondere a una domanda precedente con risposte ben definite e corrette, l'aggiunta di una nuova risposta a fine giornata potrebbe non farti ottenere alcun credito. Se hai alcune nuove informazioni distintive, o sei convinto che le altre risposte siano tutte sbagliate, aggiungi sicuramente una nuova risposta, ma "l'ennesima risposta" fornisce le stesse informazioni di base molto tempo dopo che la domanda è stata generalmente vinta " ti guadagno molto credito. Francamente, non credo ci sia nulla di nuovo in questa risposta.
Jonathan Leffler,

Beh, avrei dovuto ricordare il tuo punto - okay
Susobhan Das,

-1

Una funzione void f () compilata da un compilatore C e una funzione con lo stesso nome void f () compilata da un compilatore C ++ non sono la stessa funzione. Se hai scritto quella funzione in C e hai provato a chiamarla da C ++, il linker avrebbe cercato la funzione C ++ e non avrebbe trovato la funzione C.

extern "C" dice al compilatore C ++ che hai una funzione che è stata compilata dal compilatore C. Una volta detto che è stato compilato dal compilatore C, il compilatore C ++ saprà come chiamarlo correttamente.

Inoltre, consente al compilatore C ++ di compilare una funzione C ++ in modo tale che il compilatore C possa chiamarla. Tale funzione sarebbe ufficialmente una funzione C, ma poiché è compilata dal compilatore C ++, può utilizzare tutte le funzionalità C ++ e ha tutte le parole chiave C ++.


Il compilatore C ++ può compilare una extern "C"funzione e (soggetta ad alcuni vincoli) sarà richiamabile dal codice compilato da un compilatore C.
Jonathan Leffler
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.