Qu'est-ce qui fait exactement que mettre extern "C"
dans le code C++ ?
Par exemple :
extern "C" {
void foo();
}
Qu'est-ce qui fait exactement que mettre extern "C"
dans le code C++ ?
Par exemple :
extern "C" {
void foo();
}
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 lienextern "C"
est ignorée pour les membres de la classeextern "C"
force une fonction à avoir un lien externe (ne peut pas la rendre statique)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
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é) ?
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".
@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.
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.
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.
@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.
@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.
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.
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 ?
@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).
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++.
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.
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é .
Mangée est le terme généralement utilisé... Je ne crois pas avoir déjà vu "décoré" utilisé dans ce sens.
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 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.
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.
1 votes
@TamaMcGlinn, Rappelez-vous le vieil adage : "si c'est sur SO alors ça doit être correct". Les devs adorent pousser les infos incorrectes en haut de la liste même si nous trions par votes, et les utilisateurs adorent parfois accepter des réponses manifestement incorrectes. Si vous cherchez réel l'exactitude (ou même simplement un algorithme de tri sans bogue), alors SO est pas l'endroit idéal pour vous. La philosophie de base de ce site est essentiellement : "si vous l'aimez, alors c'est vrai pour vous". Si la majorité l'aime, alors c'est universellement vrai pour tout le monde."
0 votes
Son dernier commentaire, à savoir qu'il s'agit d'un fichier Cpp et non C, est correct. Je viens de tester, et mon compilateur C rejette la construction, en disant que le fichier Cpp n'est pas une construction.
"C"
est un identifiant inattendu. Mais bien sûr, cela n'a rien à voir avec la compilation croisée, qui consiste à compiler exactement le même code pour une architecture de CPU différente de celle qui exécute le compilateur.