1863 votes

Quel est l'effet de l'externalité "C" en C++ ?

Qu'est-ce qui fait exactement que mettre extern "C" dans le code C++ ?

Par exemple :

extern "C" {
   void foo();
}

97 votes

J'aimerais vous présenter cet article : http://www.agner.org/optimize/calling_conventions.pdf Il vous en dit beaucoup plus sur la convention d'appel et la différence entre les compilateurs.

1 votes

@Litherum Je pense qu'il s'agit de dire au compilateur de compiler cette portée de code en utilisant C, étant donné que vous avez un compilateur croisé. De plus, cela signifie que vous avez un fichier Cpp dans lequel se trouve le code suivant foo() fonction.

7 votes

@ha9u63ar C'est "du haut de ma tête". Tout le reste de votre commentaire est également incorrect. Je vous recommande de le supprimer.

1746voto

Faisal Vali Points 10048

extern "C" fait en sorte qu'un nom de fonction en C++ ait un lien C (le compilateur n'altère pas le nom) de sorte que le code C du client puisse se lier à (utiliser) votre fonction en utilisant un fichier d'en-tête compatible C qui ne contient que la 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 C du client liera ensuite en utilisant le nom C.

Étant donné que le C++ dispose d'une surcharge des noms de fonctions et que le C n'en dispose pas, le compilateur C++ ne peut pas utiliser le nom de la fonction comme un identifiant unique à lier, il déforme donc le nom en ajoutant des informations sur les arguments. Un compilateur C n'a pas besoin de déformer le nom puisque vous ne pouvez pas surcharger les noms de fonctions en C. Lorsque vous indiquez qu'une fonction a une valeur de extern "C" en C++, le compilateur C++ n'ajoute pas d'informations sur le type d'argument/paramètre au nom utilisé pour la liaison.

Juste pour que vous sachiez, vous pouvez spécifier extern "C" le lien à chaque déclaration/définition individuelle explicitement 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 les détails techniques vous intéressent, ils sont énumérés dans la section 7.5 de la norme C++03, dont voici un bref résumé (en insistant sur les points suivants extern "C" ) :

  • extern "C" est une spécification de lien
  • Chaque compilateur est requis pour assurer la liaison "C
  • Une spécification de lien ne doit apparaître que dans la portée d'un espace de nom.
  • Tous les types de fonctions, les noms de fonctions et les noms de variables ont un lien avec le langage. Voir le commentaire de Richard : Seuls les noms de fonctions et les noms de variables ayant un lien externe ont un lien avec la langue.
  • Deux types de fonctions avec des liens linguistiques distincts sont des types distincts même s'ils sont identiques par ailleurs.
  • Les fiches de liaison s'emboîtent, la fiche intérieure détermine la liaison finale.
  • extern "C" est ignorée pour les membres de la classe
  • Au maximum 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 la 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 linguistique
  • Le lien entre le langage C++ et les objets définis dans d'autres langages et entre les objets définis dans le langage C++ et ceux provenant d'autres langages est défini par l'implémentation et dépend du langage. Ce n'est que lorsque les stratégies de disposition des objets de deux implémentations de langage sont suffisamment similaires qu'un tel lien peut être réalisé.

0 votes

Est-ce qu'il y a une raison fonctionnelle pour laquelle je devrais mettre 'extern "C"' devant une fonction (autre que pour l'appeler depuis un programme C séparé) ?

27 votes

Le compilateur C n'utilise pas le mangling comme le fait le compilateur C++. Donc si vous voulez appeler une interface c à partir d'un programme c++, vous devez clairement déclarer l'interface c comme "extern c".

2 votes

@Litherum - si vous n'avez pas l'intention que votre code soit lié à un autre compilateur C++ ou à un compilateur C ou à un langage dynamique (ruby, python, etc.) pour lequel vous développez une extension, vous n'avez pas besoin de extern "C". Cela ne vous apporte rien.

375voto

UncaAlby Points 616

Je voulais juste ajouter un peu d'information, puisque je ne l'ai pas encore vu affiché.

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

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Cela 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 vous pouvez toujours l'utiliser avec votre ancien code C, où la macro est PAS défini, donc il ne verra pas la construction C++ unique.

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

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

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

Je ne suis pas sûr de la meilleure façon, mais j'ai vu les deux.

11 votes

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 où le nom de la fonction n'est pas altéré. Si vous liez ensuite des objets C et C++ avec le linker, il ne trouvera PAS les fonctions. Vous devrez inclure ces fichiers "legacy header" avec le mot-clé extern comme dans votre deuxième bloc de code.

10 votes

@Anne : Le compilateur C++ recherchera également les noms non mélangés, parce qu'il a vu que extern "C" dans l'en-tête). Cela fonctionne très bien, j'ai utilisé cette technique de nombreuses fois.

1 votes

@Ben. Je faisais référence à la phrase Not sure which way is better, but I have seen both. . Il y a une différence ! Si vous compilez le premier fichier avec gcc il n'a pas rencontré le extern "C" phrase... Donc, si vous n'utilisez pas la deuxième phrase dans votre code C++, vous avez toujours des problèmes. Il n'y a aucune différence si vous utilisez le même compilateur pour tous les fichiers.

214voto

sud03r Points 6093

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 celui de la fonction. Cela est possible car en C, deux fonctions non statiques ne peuvent pas avoir le même nom.

Comme le C++ permet la surcharge et possède de nombreuses fonctionnalités que le C ne possède pas - comme les classes, les fonctions membres, les spécifications des exceptions - il n'est pas possible d'utiliser simplement le nom de la fonction comme nom de symbole. Pour résoudre ce problème, le C++ utilise ce que l'on appelle le "name 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 à l'aspect bizarre, traitée uniquement par le compilateur et l'éditeur de liens.

Ainsi, si vous spécifiez qu'une fonction est extern C, le compilateur n'effectue pas de manipulation de nom avec elle et elle peut être directement directement en utilisant son nom de symbole comme nom de fonction.

C'est pratique quand on utilise dlsym() et dlopen() pour appeler ces fonctions.

0 votes

Qu'est-ce que vous voulez dire par "pratique" ? est-ce que le nom du symbole = le nom de la fonction ferait connaître le nom du symbole passé à la dlsym, ou autre chose ?

1 votes

@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 y a une spécification publiée de démêlage de noms sous la forme de l'ABI Itanium que tous les compilateurs x86 que je connais utilisent pour démêler les noms de fonctions C++, mais rien dans le langage ne l'exige).

31voto

Sander Points 131

Il n'est pas possible de rendre n'importe quel en-tête C compatible avec C++ en l'enveloppant simplement dans 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 fichier g++ :

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

C'est logique, mais c'est quelque chose à garder à l'esprit lors du portage du code C vers C++.

16 votes

extern "C" signifie utiliser la liaison C, comme décrit dans d'autres réponses. Cela ne signifie pas qu'il faut "compiler le contenu en C" ou autre. int virtual; est invalide en C++ et le fait de spécifier une liaison différente n'y change rien.

2 votes

... ou mode généralement, tout code contenant une erreur de syntaxe ne sera pas compilé.

4 votes

@ValentinHeinitz naturellement, bien que l'utilisation de "virtual" comme identifiant en C ne soit pas une erreur de syntaxe. Je voulais juste souligner que vous ne pouvez pas automatiquement utiliser tout L'en-tête C en C++ en mettant extern "C" autour.

30voto

Employed Russian Points 50479

Il modifie le lien d'une fonction de manière à ce que la fonction puisse être appelée depuis C. En pratique, cela signifie que le nom de la fonction n'est pas mutilé .

3 votes

Mangée est le terme généralement utilisé... Je ne crois pas avoir déjà vu "décoré" utilisé dans ce sens.

3 votes

Microsoft utilise (au moins partiellement) décoré plutôt que mangled dans leur documentation. ils nomment même leur outil pour défaire (aka un-mangle) un nom undname .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X