Question Quel est l'effet de extern "C" en C ++?


Que fait exactement mettre extern "C" dans le code C ++ faire?

Par exemple:

extern "C" {
   void foo();
}

1214
2018-06-25 02:10


origine


Réponses:


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

Comme le C ++ est surchargé de noms de fonctions et que C ne le fait pas, le compilateur C ++ ne peut pas simplement utiliser le nom de la fonction en tant qu'ID unique pour lier, donc il altère le nom en ajoutant des informations sur les arguments. Le compilateur AC n'a pas besoin d'altérer 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'informations argument / type au nom utilisé lien.

Juste pour que vous sachiez, vous pouvez spécifier explicitement le lien "C" à chaque déclaration / définition individuelle ou utiliser un bloc pour grouper une séquence de déclarations / définitions afin d'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 extern "C"):

  • extern "C" est une spécification de liaison
  • Chaque compilateur est Champs obligatoires pour fournir une liaison "C"
  • une spécification de liaison doit se produire uniquement dans l'étendue de l'espace de noms
  •  tous les types de fonctions, noms de fonctions et noms de variables ont un lien de langage   Voir le commentaire de Richard: Seuls les noms de fonction et les noms de variables avec liaison externe ont un lien de langage
  • deux types de fonctions avec des liens linguistiques distincts sont des types distincts même s'ils sont identiques
  • Nid spécification de liaison, l'interne 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 un lien "C" (indépendamment de l'espace de noms)
  •  extern "C" force une fonction à avoir un lien externe (ne peut pas le rendre statique)    Voir le commentaire de Richard:    'static' à 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 de langage
  • 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épend de la langue. Ce n'est que lorsque les stratégies de mise en page d'objets de deux implémentations de langage sont suffisamment similaires que ce lien peut être atteint

1216
2018-06-25 02:12



Je voulais juste ajouter un peu d'info, car je ne l'ai pas encore vu.

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 permet, 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 tu peux aussi toujours l'utiliser avec votre code C hérité, où la macro est NE PAS défini, de sorte qu'il ne verra pas la construction C ++ unique.

Bien que, j'ai également vu le code C ++ tel que:

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

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

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


242
2017-10-21 01:08



Dans chaque programme C ++, toutes les fonctions non statiques sont représentées dans le fichier binaire en tant que 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. C'est possible parce que dans C, deux fonctions non statiques peuvent avoir le même nom.

Parce que C ++ permet la surcharge et possède de nombreuses fonctionnalités que C ne fait 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 ce problème, C ++ utilise 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 bizarre traitée uniquement par le compilateur et l'éditeur de liens.

Donc, si vous spécifiez une fonction à extern C, le compilateur n'effectue pas de changement de nom avec lui et il peut être directement accédé en utilisant son nom de symbole comme nom de la fonction.

Cela vient à portée de main en utilisant dlsym() et dlopen() pour appeler de telles fonctions.


168
2018-06-25 05:22



Let's décompiler le fichier objet g ++ généré pour voir ce qui se passe dans cette implémentation.

Générer un exemple

Contribution:

void f() {}
void g();

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

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

Compiler avec la sortie de GCC 4.8 Linux ELF:

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

On voit ça:

  • ef et eg ont é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 étaient ne pas mutilé:

  • défini
  • déclaré mais indéfini (Ndx = UND), à fournir au lien ou à l'exécution à partir d'un autre fichier objet

Donc vous aurez besoin extern "C" les deux en appelant:

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

Choses qui ne fonctionnent pas en extern C

Il devient évident que toute fonctionnalité C ++ nécessitant 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) { }
}

120
2018-05-29 10:06



C ++ altère 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 de 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 offrent 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, car la même fonction printMe est défini deux fois (même s'ils ont des paramètres différents int a contre char a).

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

Un compilateur C ++ compilera l'exemple ci-dessus. Cela ne fait rien printMe est défini deux fois.

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

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

Extern dit "ne pas manchonner les noms de fonction"

Cependant, imaginons que nous ayons un fichier C hérité nommé "parent.c" includes noms de fonctions d'autres fichiers C hérités, "parent.h", "child.h", etc. Si le fichier hérité "parent.c" est exécuté via un compilateur C ++, les noms des fonctions seront tronqués, et ils seront écrasés. ne correspond plus aux noms de fonctions spécifiés dans "parent.h", "child.h", etc - donc les noms de fonctions dans ces fichiers externes doivent également être mutilés. Et cela pourrait devenir assez salissant. Il peut donc être pratique de fournir un mot-clé qui peut indiquer au compilateur C ++ de ne pas manipuler un nom de fonction.

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


23
2018-02-12 01:50



Il change le lien d'une fonction de telle sorte que la fonction est appelable de C. Dans la pratique cela signifie que le nom de la fonction n'est pas mutilé.


22
2018-06-25 02:12



Aucun C-header ne compilera avec extern "C". Lorsque les identifiants d'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;
};
}

Kinda a du sens, mais c'est quelque chose à garder à l'esprit lors du portage du code C en C ++.


21
2018-01-09 22:16



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 au cours de la phase de liaison.


16
2018-06-25 02:12