Quel est l'effet du «C» externe en C ++?

1632

Que fait exactement la mise extern "C"en code C ++?

Par exemple:

extern "C" {
   void foo();
}
Litherum
la source
83
Je voudrais vous présenter cet article: http://www.agner.org/optimize/calling_conventions.pdf Il vous en dit beaucoup plus sur l'appel de convention et la différence entre les compilateurs.
Sam Liao
1
@Litherum En haut de ma tête, il dit au compilateur de compiler cette étendue de code en utilisant C, étant donné que vous avez un compilateur croisé. Cela signifie également que vous avez un fichier Cpp où vous avez cette foo()fonction.
ha9u63ar
1
@ ha9u63ar C'est «du haut de ma tête». Le reste de votre commentaire est également incorrect. Je vous recommande de le supprimer.
TamaMcGlinn

Réponses:

1560

extern "C" fait un nom de fonction en C ++ avoir un lien 'C' (le compilateur ne modifie pas le nom) afin que le code C client puisse se lier à (c'est-à-dire utiliser) votre fonction en utilisant un fichier d'en-tête compatible 'C' qui contient juste le déclaration de votre fonction. Votre définition de fonction est contenue dans un format binaire (qui a été compilé par votre compilateur C ++) que l'éditeur de liens «C» du client liera ensuite à l'aide du nom «C».

Étant donné que C ++ a une surcharge de noms de fonctions et que C ne le fait pas, le compilateur C ++ ne peut pas simplement utiliser le nom de la fonction comme identifiant unique pour se lier à, il modifie donc le nom en ajoutant des informations sur les arguments. Le compilateur AC n'a pas besoin de modifier le nom car vous ne pouvez pas surcharger les noms de fonction en C. Lorsque vous indiquez qu'une fonction possède une liaison externe "C" en C ++, le compilateur C ++ n'ajoute pas d'informations d'argument / type de paramètre au nom utilisé pour lien.

Juste pour que vous le sachiez, vous pouvez spécifier explicitement le lien "C" à chaque déclaration / définition individuelle ou utiliser un bloc pour regrouper une séquence de déclarations / définitions pour avoir un certain lien:

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

Si vous vous souciez des détails techniques, ils sont répertoriés dans la section 7.5 de la norme C ++ 03, voici un bref résumé (en mettant l'accent sur le "C" externe):

  • extern "C" est une spécification de liaison
  • Chaque compilateur est tenu de fournir une liaison "C"
  • une spécification de liaison doit se produire uniquement dans la portée de l'espace de noms
  • tous les types de fonction, les noms de fonction et les noms de variable ont une liaison de langage Voir le commentaire de Richard: seuls les noms de fonction et les noms de variable avec liaison externe ont une liaison de langue
  • deux types de fonctions avec des liens de langage distincts sont des types distincts même s'ils sont par ailleurs identiques
  • imbrication des spécifications de liaison, une intérieure détermine la liaison finale
  • extern "C" est ignoré pour les membres de la classe
  • au plus une fonction avec un nom particulier peut avoir une liaison "C" (quel que soit l'espace de noms)
  • extern "C" force une fonction à avoir un lien externe (ne peut pas la rendre statique) Voir le commentaire de Richard: "statique" à l'intérieur de "extern" C "'est valide; une entité ainsi déclarée a un lien interne et n'a donc pas de lien linguistique
  • La liaison de C ++ à des objets définis dans d'autres langages et à des objets définis en C ++ à partir d'autres langages est définie par l'implémentation et dépendante du langage. Ce n'est que lorsque les stratégies de disposition des objets de deux implémentations de langage sont suffisamment similaires qu'une telle liaison peut être réalisée
Faisal Vali
la source
22
Le compilateur C n'utilise pas le mangling, ce que fait C ++. Donc, si vous voulez appeler l'interface ac à partir d'un programme c ++, vous devez clairement déclarer que l'interface c est "extern c".
Sam Liao
59
@Faisal: n'essayez pas de lier du code construit avec différents compilateurs C ++, même si les références croisées sont toutes 'extern "C"'. Il existe souvent des différences entre les dispositions des classes, ou les mécanismes utilisés pour gérer les exceptions, ou les mécanismes utilisés pour garantir que les variables sont initialisées avant utilisation, ou d'autres différences de ce type, en plus vous pourriez avoir besoin de deux bibliothèques de support d'exécution C ++ distinctes (une pour chaque compilateur).
Jonathan Leffler
8
'extern "C" force une fonction à avoir une liaison externe (ne peut pas la rendre statique)' est incorrect. 'statique' à l'intérieur de 'extern "C"' est valide; une entité ainsi déclarée a un lien interne et n'a donc pas de lien linguistique.
Richard Smith
14
«tous les types de fonction, les noms de fonction et les noms de variable ont un lien de langue» est également incorrect. Seuls les noms de fonction et les noms de variable avec liaison externe ont une liaison de langue.
Richard Smith
9
Notez que extern "C" { int i; }c'est une définition. Ce n'est peut-être pas ce que vous vouliez, à côté de la non-définition de void g(char);. Pour en faire une non-définition, vous auriez besoin extern "C" { extern int i; }. D'un autre côté, la syntaxe à une déclaration sans accolades fait de la déclaration une non-définition: extern "C" int i;est la même queextern "C" { extern int i; }
aschepler
327

Je voulais juste ajouter un peu d'informations, car je ne les ai pas encore vues.

Vous verrez très souvent du code dans les en-têtes C comme ceci:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Ce que cela accomplit, c'est qu'il vous permet d'utiliser ce fichier d'en-tête C avec votre code C ++, car la macro "__cplusplus" sera définie. Mais vous pouvez également l' utiliser avec votre code C hérité, où la macro n'est PAS définie, de sorte qu'il ne verra pas la construction C ++ uniquement.

Cependant, j'ai également vu du code C ++ tel que:

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

que j'imagine accomplit à peu près la même chose.

Je ne sais pas quel chemin est meilleur, mais j'ai vu les deux.

UncaAlby
la source
11
Il y a une nette différence. Dans le premier cas, si vous compilez ce fichier avec le compilateur gcc normal, il générera un objet dont le nom de la fonction n'est pas modifié. Si vous liez ensuite des objets C et C ++ avec l'éditeur de liens, il ne trouvera PAS les fonctions. Vous devrez inclure ces fichiers "en-tête hérités" avec le mot-clé extern comme dans votre deuxième bloc de code.
Anne van Rossum du
8
@Anne: Le compilateur C ++ recherchera également les noms démêlés, car il l'a vu extern "C"dans l'en-tête). Cela fonctionne très bien, a utilisé cette technique plusieurs fois.
Ben Voigt
20
@Anne: Ce n'est pas vrai, le premier est bien aussi. Il est ignoré par le compilateur C et a le même effet que le second en C ++. Le compilateur se fiche de savoir s'il rencontre extern "C"avant ou après qu'il inclut l'en-tête. Au moment où il atteint le compilateur, ce n'est de toute façon qu'un long flux de texte prétraité.
Ben Voigt
8
@Anne, non, je pense que vous avez été affecté par une autre erreur dans la source, car ce que vous décrivez est faux. Aucune version de ne g++s'est trompée, pour aucune cible, à aucun moment au cours des 17 dernières années au moins. L'intérêt du premier exemple est que peu importe que vous utilisiez un compilateur C ou C ++, aucun changement de nom ne sera effectué pour les noms dans le extern "C"bloc.
Jonathan Wakely
7
"lequel est le meilleur" - à coup sûr, la première variante est meilleure: elle permet d'inclure directement l'en-tête, sans aucune autre exigence, à la fois en code C et C ++. La deuxième approche est une solution de contournement pour les en-têtes C, l'auteur a oublié les gardes C ++ (pas de problème, cependant, si ceux-ci sont ajoutés par la suite, les déclarations externes "C" imbriquées sont acceptées ...).
Aconcagua
267

Décompiler un g++binaire généré pour voir ce qui se passe

main.cpp

void f() {}
void g();

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

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

Compilez et démontez la sortie ELF générée:

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

La sortie contient:

     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

Interprétation

On voit ça:

  • efet egont été stockés dans des symboles avec le même nom que dans le code

  • les autres symboles ont été mutilés. Démêlons-les:

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

Conclusion: les deux types de symboles suivants n'ont pas été mutilés:

  • défini
  • déclaré mais non défini ( Ndx = UND), à fournir lors de la liaison ou de l'exécution à partir d'un autre fichier objet

Vous aurez donc besoin des extern "C"deux lors de votre appel:

  • C de C ++: dites g++d'attendre des symboles démêlés produits pargcc
  • C ++ à partir de C: dites g++de générer des symboles démêlés gccà utiliser

Choses qui ne fonctionnent pas en externe C

Il devient évident que toute fonctionnalité C ++ qui nécessite un changement de nom ne fonctionnera pas à l'intérieur 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) { }
}

Exemple C exécutable minimal à partir de C ++

Par souci d'exhaustivité et pour les nouveautés, voir aussi: Comment utiliser les fichiers source C dans un projet C ++?

L'appel de C à partir de C ++ est assez simple: chaque fonction C n'a qu'un seul symbole non mutilé possible, donc aucun travail supplémentaire n'est requis.

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

Courir:

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

Sans extern "C"le lien échoue avec:

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

car g++s'attend à trouver un mutilé f, qui gccn'a pas produit.

Exemple sur GitHub .

Exemple de C ++ exécutable minimal à partir de C

Appeler C ++ à partir de C est un peu plus difficile: nous devons créer manuellement des versions non mutilées de chaque fonction que nous voulons exposer.

Nous illustrons ici comment exposer les surcharges de fonctions C ++ à C.

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

Courir:

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

Sans extern "C"cela il échoue avec:

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

car g++généré des symboles mutilés qui gccne peuvent pas trouver.

Exemple sur GitHub .

Testé dans Ubuntu 18.04.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
21
Meilleure réponse puisque vous 1) mentionnez explicitement que cela extern "C" {vous aide à appeler des fonctions C démêlées à partir de programmes C ++ , ainsi que des fonctions C ++ démêlées à partir de programmes C , que les autres réponses ne rendent pas si évidentes, et 2) parce que vous montrez des exemples distincts de chaque. Merci!
Gabriel Staples
3
J'aime beaucoup cette réponse
selfboot
4
De loin la meilleure réponse car elle montre comment appeler les fonctions surchargées de c
Gaspa79
1
@JaveneCPPMcGowan qu'est-ce qui vous fait penser que j'avais un professeur C ++? :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
205

Dans chaque programme C ++, toutes les fonctions non statiques sont représentées dans le fichier binaire sous forme de symboles. Ces symboles sont des chaînes de texte spéciales qui identifient de manière unique une fonction dans le programme.

En C, le nom du symbole est le même que le nom de la fonction. Ceci est possible car en C aucune fonction non statique ne peut avoir le même nom.

Parce que C ++ permet la surcharge et possède de nombreuses fonctionnalités que C n'a pas - comme les classes, les fonctions membres, les spécifications d'exception - il n'est pas possible d'utiliser simplement le nom de la fonction comme nom de symbole. Pour résoudre cela, C ++ utilise ce que l'on appelle le mangling de nom, qui transforme le nom de la fonction et toutes les informations nécessaires (comme le nombre et la taille des arguments) en une chaîne d'aspect étrange traitée uniquement par le compilateur et l'éditeur de liens.

Donc, si vous spécifiez une fonction pour être un C externe, le compilateur n'effectue pas de changement de nom avec elle et il est directement accessible en utilisant son nom de symbole comme nom de fonction.

Cela est pratique lors de l'utilisation dlsym()et dlopen()de l'appel de ces fonctions.

sud03r
la source
que voulez-vous dire par pratique? le nom du symbole = le nom de la fonction ferait-il passer le nom du symbole à dlsym, ou autre chose?
Erreur
1
@Error: oui. Il est essentiellement impossible dans le cas général de dlopen () une bibliothèque partagée C ++ avec seulement un fichier d'en-tête et de choisir la bonne fonction à charger. (Sur x86, il existe une spécification de gestion de nom publiée sous la forme de Itanium ABI que tous les compilateurs x86 que je connais utilisent pour gérer les noms de fonction C ++, mais rien dans le langage ne l'exige.)
Jonathan Tomer
52

C ++ modifie les noms de fonction pour créer un langage orienté objet à partir d'un langage procédural

La plupart des langages de programmation ne sont pas construits sur les langages de programmation existants. C ++ est construit au-dessus de C, et en plus c'est un langage de programmation orienté objet construit à partir d'un langage de programmation procédural, et pour cette raison il existe des expressions C ++ comme extern "C"qui offrent une compatibilité descendante avec C.

Regardons l'exemple suivant:

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

Le compilateur AC ne compilera pas l'exemple ci-dessus, car la même fonction printMeest définie deux fois (même s'ils ont des paramètres différents par int arapport à char a).

gcc -o printMe printMe.c && ./printMe;
1 erreur. PrintMe est défini plusieurs fois.

Un compilateur C ++ compilera l'exemple ci-dessus. Il ne se soucie pas de ce qui printMeest défini deux fois.

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

En effet, un compilateur C ++ renomme implicitement ( mangles ) les fonctions en fonction de leurs paramètres. En C, cette fonctionnalité n'était pas prise en charge. Cependant, lorsque C ++ a été construit sur C, le langage a été conçu pour être orienté objet et devait prendre en charge la possibilité de créer différentes classes avec des méthodes (fonctions) du même nom, et de remplacer les méthodes (substitution de méthode ) basées sur différents paramètres.

extern "C" dit "ne pas modifier les noms de fonction C"

Cependant, imaginez que nous avons un fichier C hérité nommé "parent.c" qui includes noms de fonction à partir d'autres fichiers C hérités, "parent.h", "child.h", etc. Si le fichier "parent.c" hérité est exécuté via un compilateur C ++, les noms de fonction seront modifiés et ils ne correspondront plus aux noms de fonction spécifiés dans "parent.h", "child.h", etc. - donc les noms de fonction dans ces fichiers externes devront également être mutilé. La gestion des noms de fonction dans un programme C complexe, ceux avec beaucoup de dépendances, peut conduire à un code cassé; il peut donc être pratique de fournir un mot-clé qui peut indiquer au compilateur C ++ de ne pas modifier un nom de fonction.

Le extern "C"mot clé indique à un compilateur C ++ de ne pas modifier (renommer) les noms de fonction C.

Par exemple:

extern "C" void printMe(int a);

tfmontague
la source
pouvons-nous ne pas utiliser extern "C"si nous avons juste un dllfichier? Je veux dire si nous n'avons pas de fichier d'en-tête et avons juste un fichier source (juste des implémentations) et l'utilisation de sa fonction via le pointeur de fonction. dans cet état, nous venons d'utiliser des fonctions (quel que soit son nom).
BattleTested
@tfmontague, pour moi, vous avez bien compris! droit dans la tête.
Artanis Zeratul
29

Aucun en-tête C ne peut être rendu compatible avec C ++ en enveloppant simplement le "C" externe. Lorsque des identificateurs dans un en-tête C entrent en conflit avec des mots clés C ++, le compilateur C ++ s'en plaindra.

Par exemple, j'ai vu le code suivant échouer dans un g ++:

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

C'est un peu logique, mais c'est quelque chose à garder à l'esprit lors du portage de code C en C ++.

Sander Mertens
la source
14
extern "C"signifie utiliser la liaison C, comme décrit par d'autres réponses. Cela ne signifie pas «compiler le contenu en C» ou quoi que ce soit. int virtual;n'est pas valide en C ++ et la spécification d'une liaison différente ne change rien à cela.
MM
1
... ou le mode en général, tout code contenant une erreur de syntaxe ne sera pas compilé.
Valentin Heinitz
4
@ValentinHeinitz naturellement, bien que l'utilisation de "virtuel" comme identifiant en C ne soit pas une erreur de syntaxe. Je voulais juste souligner que vous ne pouvez pas utiliser automatiquement un en- tête C en C ++ en mettant un "C" externe autour de lui.
Sander Mertens
28

Il modifie le lien d'une fonction de telle sorte que la fonction peut être appelée à partir de C. En pratique, cela signifie que le nom de la fonction n'est pas modifié .

Russe employé
la source
3
Mangled est le terme généralement utilisé ... Ne croyez pas que j'aie jamais vu «décoré» utilisé avec ce sens.
Matthew Scharley
1
Microsoft (au moins partiellement) utilise des éléments décorés plutôt que mutilés dans leur documentation. ils nomment même leur outil pour décorer (alias dé-modifier) ​​un nom undname.
René Nyffenegger
20

Il informe le compilateur C ++ de rechercher les noms de ces fonctions dans un style C lors de la liaison, car les noms des fonctions compilées en C et C ++ sont différents pendant l'étape de liaison.

Mark Rushakoff
la source
12

extern "C" est destiné à être reconnu par un compilateur C ++ et à informer le compilateur que la fonction notée est (ou doit être) compilée en style C. Ainsi, lors de la liaison, il renvoie à la version correcte de la fonction de C.

Flami
la source
6

J'ai utilisé 'extern "C"' avant pour les fichiers dll (bibliothèque de liens dynamiques) pour rendre la fonction etc. main () "exportable" afin qu'elle puisse être utilisée plus tard dans un autre exécutable de dll. Peut-être qu'un exemple de l'endroit où je l'utilisais peut être 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);
}
SturmCoder
la source
4
Faux. extern "C"et ne __declspec(dllexport)sont pas liés. Le premier contrôle la décoration des symboles, le second est responsable de la création d'une entrée d'exportation. Vous pouvez également exporter un symbole en utilisant la décoration de nom C ++. En plus de manquer complètement le point de cette question, il y a aussi d'autres erreurs dans l'exemple de code. D'une part, mainexporté à partir de votre DLL ne déclare pas de valeur de retour. Ou appeler convention, d'ailleurs. Lors de l'importation, vous attribuez une convention d'appel aléatoire ( WINAPI) et utilisez le mauvais symbole pour les générations 32 bits (devrait être _mainou _main@0). Désolé, -1.
IInspectable
1
Cela ne fait que répéter, que vous ne savez pas, ce que vous faites, mais le faire de cette façon semble fonctionner pour vous, pour une liste non divulguée de plates-formes cibles. Vous n'avez pas abordé les questions que j'ai soulevées dans mon commentaire précédent. Il s'agit toujours d'un vote à la baisse, car il est extrêmement faux (il y a plus, cela ne correspondait pas à un seul commentaire).
IIspectable
1
Publier une réponse sur Stack Overflow implique que vous savez ce que vous faites. C'est attendu. Quant à votre tentative "pour empêcher la corruption de la pile lors de l'exécution" : votre signature de fonction spécifie une valeur de retour de type void*, mais votre implémentation ne renvoie rien. Ça va très bien voler ...
IIspectable
1
Si vous implémentez quelque chose qui semble fonctionner, par pure chance, alors vous ne savez clairement pas ce que vous faites (votre échantillon "de travail" tombe dans cette catégorie). C'est un comportement indéfini, et sembler fonctionner est une forme valide de comportement indéfini. C'est encore indéfini. Je vous serais très reconnaissant si vous faisiez preuve de plus de diligence à l'avenir. Une partie de cela pourrait être la suppression de cette réponse proposée.
IInspectable
1
Vous réinterprétez une fonction qui ne renvoie rien en tant que fonction qui renvoie un pointeur. C'est une pure chance, que x86 soit très indulgent en ce qui concerne les signatures de fonction non concordantes, et en particulier les valeurs de retour de type intégral. Votre code ne fonctionne que par coïncidence. Si vous n'êtes pas d'accord, vous devez expliquer pourquoi votre code fonctionne de manière fiable.
IInspectable
5

extern "C"est une spécification de liaison utilisée pour appeler des fonctions C dans les fichiers source Cpp . Nous pouvons appeler des fonctions C, écrire des variables et inclure des en-têtes . La fonction est déclarée dans l'entité externe et elle est définie à l'extérieur. La syntaxe est

Type 1:

extern "language" function-prototype

Type 2:

extern "language"
{
     function-prototype
};

par exemple:

#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)
{
    //
    //
}
Yogeesh HT
la source
3

Cette réponse est pour les impatients / ont des délais à respecter, seule une partie / explication simple est ci-dessous:

  • en C ++, vous pouvez avoir le même nom en classe via une surcharge (par exemple, car ils sont tous du même nom ne peuvent pas être exportés tels quels à partir de dll, etc.) la solution à ces problèmes est qu'ils sont convertis en différentes chaînes (appelées symboles ), les symboles représentent le nom de la fonction, ainsi que les arguments, de sorte que chacune de ces fonctions, même avec le même nom, peut être identifiée de manière unique (également appelée, mangling de nom)
  • en C, vous n'avez pas de surcharge, le nom de la fonction est unique (donc, une chaîne distincte pour identifier le nom de la fonction de manière unique n'est pas requise, donc le symbole est le nom de la fonction lui-même)

Donc,
en C ++, avec le nom qui identifie de façon unique les identités de chaque fonction
en C, même sans le nom qui identifie de manière unique chaque fonction

Pour modifier le comportement de C ++, c'est-à-dire pour spécifier que le changement de nom ne doit pas se produire pour une fonction particulière, vous pouvez utiliser extern "C" avant le nom de la fonction, pour une raison quelconque, comme exporter une fonction avec un nom spécifique à partir d'une DLL , à l'usage de ses clients.

Lisez les autres réponses, pour des réponses plus détaillées / plus correctes.

Manohar Reddy Poreddy
la source
1

Lors du mélange de C et C ++ (c'est-à-dire, a. Appelant la fonction C à partir de C ++; et b. Appelant la fonction C ++ à partir de C), le changement de nom C ++ provoque des problèmes de liaison. Techniquement parlant, ce problème se produit uniquement lorsque les fonctions appelées ont déjà été compilées en binaire (très probablement, un fichier de bibliothèque * .a) à l'aide du compilateur correspondant.

Nous devons donc utiliser extern "C" pour désactiver le changement de nom en C ++.

Trombe
la source
0

Sans entrer en conflit avec d'autres bonnes réponses, j'ajouterai un peu de mon exemple.

Ce que fait exactement le compilateur C ++ : il modifie les noms dans le processus de compilation, d'où la nécessité de dire au compilateur de traiter l' C implémentation spécialement.

Lorsque nous créons et ajoutons des classes C ++ extern "C", nous disons à notre compilateur C ++ que nous utilisons la convention d'appel C.

Raison (nous appelons l'implémentation C depuis C ++): soit nous voulons appeler la fonction C depuis C ++, soit nous appelons la fonction C ++ depuis C (les classes C ++ ... etc ne fonctionnent pas en C).

Susobhan Das
la source
Bienvenue dans Stack Overflow. Si vous décidez de répondre à une question plus ancienne qui a des réponses bien établies et correctes, l'ajout d'une nouvelle réponse en fin de journée peut ne pas vous valoir de crédit. Si vous avez de nouvelles informations distinctives, ou si vous êtes convaincu que les autres réponses sont toutes fausses, ajoutez certainement une nouvelle réponse, mais `` encore une autre réponse '' donnant les mêmes informations de base longtemps après que la question a été posée est généralement gagnée '' t vous gagner beaucoup de crédit. Franchement, je ne pense pas qu'il y ait quelque chose de nouveau dans cette réponse.
Jonathan Leffler
Eh bien, j'aurais dû me souvenir de votre point - d'accord
Susobhan Das
-1

Une fonction void f () compilée par un compilateur C et une fonction du même nom void f () compilée par un compilateur C ++ ne sont pas la même fonction. Si vous avez écrit cette fonction en C, puis que vous avez essayé de l'appeler à partir de C ++, l'éditeur de liens rechercherait la fonction C ++ et ne trouverait pas la fonction C.

extern "C" indique au compilateur C ++ que vous avez une fonction qui a été compilée par le compilateur C. Une fois que vous lui avez dit qu'il a été compilé par le compilateur C, le compilateur C ++ saura comment l'appeler correctement.

Il permet également au compilateur C ++ de compiler une fonction C ++ de telle manière que le compilateur C puisse l'appeler. Cette fonction serait officiellement une fonction C, mais puisqu'elle est compilée par le compilateur C ++, elle peut utiliser toutes les fonctionnalités C ++ et possède tous les mots clés C ++.

gnasher729
la source
Le compilateur C ++ peut compiler une extern "C"fonction - et (sous réserve de certaines contraintes) il sera appelable par du code compilé par un compilateur C.
Jonathan Leffler