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();
}
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 linguistiquevoir 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
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.
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.
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
eteg
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 pargcc
- C++ à partir de C: dire
g++
pour générer unmangled symbolesgcc
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.
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.
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);
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é .
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++.
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.
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.
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);
}
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)
{
//
//
}
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++.
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.