Quel est l'effet de l'externe "C" en C++?

Que fait exactement extern "c/" class="blnk">C" dans le code C++?

par exemple:

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

13 réponses

extern "C" fait un nom de fonction en C++ ont 'C' linkage (compilateur ne manipule pas le nom) de sorte que le code C du client peut se lier à (I. e utilisation) votre fonction en utilisant un fichier d'en-tête compatible' C ' qui contient juste la 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 le linker 'C' du client liera ensuite en utilisant le nom 'C'.

depuis C++ a la surcharge des noms de fonction et C ne non, le compilateur C++ ne peut pas simplement utiliser le nom de la fonction comme un id unique pour le lier, donc il manipule le nom en ajoutant des informations sur les arguments. Un compilateur C n'a pas besoin de manipuler le nom puisque vous ne pouvez pas surcharger les noms de fonction en C. Lorsque vous déclarez qu'une fonction a un lien externe "C" en C++, le compilateur C++ n'ajoute pas d'information de type argument/paramètre au nom utilisé pour le lien.

juste pour que vous le sachiez, vous pouvez spécifier le lien "C" à chaque individu déclaration / définition explicitement ou en utilisant un bloc pour grouper 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 listés dans la section 7.5 de la norme C++03, voici un bref résumé (en mettant l'accent sur " C "externe):

  • extern "C" est un lien-spécification
  • chaque compilateur est requis pour fournir une liaison "C"
  • une liaison cahier des charges doivent se produire que dans l'espace de noms portée
  • tous les types de fonctions, les noms de fonctions et les noms de variables ont un lien linguistique voir le commentaire de Richard: seuls les noms de fonctions et les noms de variables avec un lien externe ont un lien linguistique
  • deux types de fonctions avec des liens linguistiques distincts sont des types distincts même si autrement identique
  • lien spécifications nid, intérieur détermine la finale de liaison
  • extern "C" est ignorée pour les membres de la classe
  • au plus une fonction avec un nom particulier peut avoir un lien" C "(indépendamment de l'Espace-nom)
  • extern "C" forces d'une fonction externe de la liaison (elle ne peut pas être statique) Voir Richard commentaire: "statique" à l'intérieur de 'extern' C 'est valide; une entité ainsi déclarée a un lien interne, et donc n'a pas de lien linguistique
  • Lien à partir de C++ pour les objets définis dans d'autres langues et d'objets définis en C++ à partir d'autres langues est mise en œuvre défini et en fonction de la langue. Ce n'est que lorsque les stratégies de mise en page des objets de deux implémentations linguistiques sont suffisamment similaires qu'un tel lien peut être établi
1257
répondu Faisal Vali 2017-05-23 12:02:57

je voulais juste ajouter un peu d'info, puisque je ne l'ai pas encore vu posté.

vous verrez très souvent le 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 est qu'il vous permet d'utiliser ce fichier d'en-tête C avec votre code C++, parce que la macro" __cpluslus " sera définie. Mais vous pouvez aussi toujours l'utiliser avec votre code C, où la macro est pas défini, donc il ne verra pas la construction unique de C++.

bien que, j'ai aussi vu le code C++ tel que:

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

qui, je l'imagine, accomplit à peu près la même chose.

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

256
répondu UncaAlby 2012-10-21 01:08:32

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 façon unique une fonction dans le programme.

en C, le nom du symbole est le même que le nom de la fonction. Cela est possible parce que dans C Aucune deux fonctions non statiques peuvent avoir le même nom.

parce que C++ permet la surcharge et a de nombreuses fonctionnalités que C n'aime pas-classes, fonctions de membre, spécifications d'exception-il n'est pas possible d'utiliser simplement le nom de la fonction comme nom du symbole. Pour résoudre cela, C++ utilise ce qu'on appelle le nom mangling, 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 de caractères bizarre traitée seulement par le compilateur et le linker.

donc si vous spécifiez une fonction à être externe, le compilateur n'exécute pas nom mangling avec elle et il peut être directement accessible à l'aide d' son nom de symbole comme nom de fonction.

cela est pratique en utilisant dlsym() et dlopen() pour appeler de telles fonctions.

177
répondu sud03r 2018-03-03 15:48:25

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

entrée:

void f() {}
void g();

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

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

compiler avec GCC 4.8 sortie ELF Linux:

g++ -c a.cpp

décompiler la table de symboles:

readelf -s a.o

la sortie contient:

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

interprétation

nous voyons que:

  • ef et eg ont été stockées dans des symboles avec le même nom que dans le code

  • "
  • les autres symboles ont été mutilés. Démontons-les:

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

Conclusion: les deux types de symboles suivants étaient et non mutilés:

  • défini
  • déclaré Mais non défini ( Ndx = UND ), à fournir au moment du lien ou de l'exécution à partir d'un autre fichier d'objet

vous aurez donc besoin de extern "C" à la fois en appelant:

  • C de C++: dire g++ de s'attendre à ce unmangled symboles produits par gcc
  • C++ à partir de C: dire g++ pour générer unmangled symboles gcc utiliser

des Choses qui ne fonctionnent pas dans extern C

il devient évident que toute fonctionnalité C++ nécessitant un changement de nom ne fonctionnera pas à l'intérieur de 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 praticable C de C++ exemple

Par souci d'exhaustivité et pour les newbs.

appeler C à partir de C++ est assez facile: chaque fonction C n'a qu'une possibilité symbole non-mutilé, donc aucun travail supplémentaire n'est nécessaire.

principal.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

sans extern "C" le lien ne fonctionne pas avec:

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

parce que g++ s'attend à trouver un f mutilé , que gcc n'a pas produit.

exemple sur GitHub .

Minimal praticable en C++ en C exemple

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

ici, nous illustrons comment exposer les surcharges de la fonction 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;
}

rpc.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

rpc.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

sans extern "C" il ne fonctionne pas avec:

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

parce que g++ a généré des symboles mutilés que gcc ne trouve pas.

exemple sur GitHub .

131

c++ liste des 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 y a des mots clés C++ comme extern qui fournissent une compatibilité ascendante 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;
}

un compilateur C ne compilera pas l'exemple ci-dessus, parce que la même fonction printMe est définie deux fois (même si elles ont des paramètres différents int a vs char a ).

gcc-o printMe printMe.C. && / printMe;

1 erreur. PrintMe est défini plus d'une fois.

un compilateur C++ compiler l'exemple ci-dessus. Il s'en fiche que printMe soit défini deux fois.

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

c'est parce qu'un compilateur C++ renomme implicitement ( mangles ) des fonctions basées sur leurs paramètres. En C, cette fonctionnalité n'était pas supportée. Cependant, lorsque C++ a été construit sur C, le langage a été conçu pour être orienté objet, et nécessaire pour soutenir la capacité pour créer des classes différentes avec des méthodes (fonctions) du même nom, et pour outrepasser les méthodes ( method overriding ) basées sur des paramètres différents.

Extern dit "ne pas marquer les noms de fonction"

cependant, imaginez que nous ayons un fichier c appelé "parent".c" que include s les noms de fonction à partir de l'ancien C fichiers, parent".h", "enfant.h", etc. Si l'héritage "parents.c" fichier est exécuté par un compilateur C++, puis le les noms de fonction seront modifiés et ne correspondront plus aux noms de fonction spécifiés dans "parent".h", "enfant.h", etc-ainsi les noms de fonction dans ces dossiers externes devraient également être mutilés. Modifier les noms de fonction à travers un programme C complexe, ceux avec beaucoup de dépendances, peut conduire à du code cassé; il pourrait donc être pratique de fournir un mot-clé qui peut dire au compilateur C++ de ne pas modifier un nom de fonction.

le mot-clé extern indique un compilateur C++ ne pas modifier (renommer) les noms de fonction. Exemple d'utilisation: extern void printMe(int a);

25
répondu tfmontague 2018-09-28 19:44:06

il modifie la liaison d'une fonction de telle manière que la fonction est appelable à partir de C. en pratique, cela signifie que le nom de la fonction n'est pas mutilé .

24
répondu Employed Russian 2009-06-25 02:12:21

aucun en-tête C ne sera compilé avec "C"externe. Lorsque des identificateurs dans un en-tête c entrent en conflit avec des mots-clés C++, le compilateur C++ se plaindra de cette situation.

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

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

a un peu de sens, mais c'est quelque chose à garder à l'esprit quand on transfère du C-code en C++.

22
répondu Sander Mertens 2015-01-26 22:22:21

il informe le compilateur C++ de rechercher les noms de ces fonctions dans un style C lors de la liaison, parce que les noms des fonctions compilées en C et C++ sont différents pendant la phase de liaison.

17
répondu Mark Rushakoff 2009-06-25 02:12:12

extern "C" est destiné à être reconnu par un compilateur C++ et à notifier au compilateur que la fonction notée est (ou sera) compilée dans le style C. De sorte que tout en liant, il lien à la version correcte de la fonction de C.

12
répondu Flami 2012-04-10 09:46:10

j'ai utilisé 'extern" C "' avant pour dll(dynamic link library) fichiers à faire etc. main () Fonction "exportable" de sorte qu'il peut être utilisé 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);
}
5
répondu SturmCoder 2014-07-01 15:43:34

extern "C" est une spécification de liaison qui est utilisée pour appeler des fonctions C dans les Cpp source files . 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)
{
    //
    //
}
4
répondu Yogeesh H T 2015-11-17 12:09:03

quand on mélange C et c++ (i.e., A. appel de la fonction C à partir de C++; et B. l'appel de la fonction C++ à partir de C), la modification du nom C++ provoque des problèmes de liaison. Techniquement parlant, ce problème ne se pose que lorsque les fonctions callee ont déjà été compilées en binaire (très probablement un*).un fichier de bibliothèque) avec le compilateur.

nous avons donc besoin d'utiliser" C " externe pour désactiver le nom mangling en C++.

1
répondu Trombe 2017-08-07 07:54:12

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

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

Donc

en C++, avec le nom du modificateur unique des identités de chaque fonction

en C, même sans le nom du modificateur unique des identités de chaque fonction

pour changer le comportement de C++, c'est-à-dire, pour spécifier que le nom mangling ne devrait pas arriver pour une fonction particulière, vous pouvez utiliser externe" C " avant le nom de la fonction, pour quelque raison que ce soit, comme exporter une fonction avec un nom spécifique à partir d'une dll, pour l'usage de ses clients.

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

1
répondu Manohar Reddy Poreddy 2018-07-24 13:41:42