web-dev-qa-db-fra.com

Comment fonctionne une déclaration "C" externe?

Je suis un cours de langage de programmation et nous parlons de la déclaration extern "C"

Comment cette déclaration fonctionne-t-elle à un niveau plus profond que "cela interfère C et C++"? Comment cela affecte-t-il les liaisons qui ont lieu dans le programme?

23
samoz

extern "C" est utilisé pour s'assurer que les symboles suivants ne sont pas mutilés (décorés).


Exemple: 

Disons que nous avons le code suivant dans un fichier appelé test.cpp

extern "C" {
  int foo() {
    return 1;
  }
}

int bar() {
  return 1;
}

Si vous exécutez gcc -c test.cpp -o test.o

Regardez les noms des symboles:

00000010 T _Z3barv

00000000 T foo

foo() conserve son nom.

45
Bertrand Marron

Regardons une fonction typique qui peut compiler à la fois en C et C++:

int Add (int a, int b)
{
    return a+b;
}

Maintenant en C la fonction s'appelle "_Add" en interne. Tandis que la fonction C++ est appelée quelque chose de complètement différent en interne en utilisant un système appelé gestion de nom. C'est en gros un moyen de nommer une fonction afin que la même fonction avec des paramètres différents porte un nom interne différent.

Donc, si Add () est défini dans add.c et que vous avez le prototype dans add.h, vous rencontrerez un problème si vous essayez d'inclure add.h dans un fichier C++. Parce que le code C++ recherche une fonction avec un nom différent de celui de add.c, vous obtiendrez une erreur de l'éditeur de liens. Pour résoudre ce problème, vous devez inclure add.c par cette méthode:

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

Maintenant, le code C++ sera lié à _Add au lieu de la version mutilée du nom C++.

C'est l'une des utilisations de l'expression. En bout de ligne, si vous devez compiler du code strictement C dans un programme C++ (via une instruction include ou un autre moyen), vous devez l'envelopper avec une déclaration extern "C" {...}.

24
Cthutu

Lorsque vous marquez un bloc de code avec extern "C", vous indiquez au système d'utiliser la liaison de style C.

Cela affecte principalement la façon dont l'éditeur de liens modifie les noms. Au lieu d'utiliser la gestion de noms de style C++ (ce qui est plus complexe pour prendre en charge les surcharges d'opérateurs), vous obtenez le nommage standard de style C à partir de l'éditeur de liens.

9
Reed Copsey

En C++, le nom/symbole des fonctions est en fait renommé, ce qui permet à différentes classes/espaces de nommage d'avoir des fonctions de même signature. En C, les fonctions sont toutes définies globalement et aucun processus de renommage personnalisé n'est nécessaire.

Pour que C++ et C se parlent, "extern C" demande au compilateur de ne pas utiliser la convention C.

5
gilbertc

Il est à noter que extern "C" modifie également les types de fonctions. Cela ne modifie pas seulement les choses aux niveaux inférieurs:

extern "C" typedef void (*function_ptr_t)();

void foo();

int main() { function_ptr_t fptr = &foo; } // error!

Le type de &foo ne correspond pas au type désigné par la typedef (bien que le code soit accepté par certains, mais pas par tous les compilateurs).

extern C affecte la gestion des noms par le compilateur C++. C'est un moyen de faire en sorte que le compilateur C++ ne modifie pas les noms, ou plutôt les modifie de la même manière qu'un compilateur C. C'est la façon dont il s'interface C et C++.

Par exemple:

extern "C" void foo(int i);

permettra à la fonction d'être implémentée dans un module C, mais lui permettra d'être appelée à partir d'un module C++.

Le problème survient lorsque l'on essaie de faire appeler par un module C une fonction C++ (le C ne pouvant évidemment pas utiliser les classes C++) définie dans un module C++. Le compilateur C n'aime pas extern "C".

Donc, vous devez utiliser ceci:

#ifdef __cplusplus
extern "C" {
#endif

void foo(int i);

#ifdef __cplusplus
}
#endif

Désormais, lorsque cela apparaît dans un fichier d’en-tête, les compilateurs C et C++ seront satisfaits de la déclaration. Elle peut maintenant être définie dans un module C ou C++ et peut être appelée à la fois par le code C et C++.

4
quamrana

extern "C" indique que le code inclus utilise une liaison de type C et une modification de nom. C++ utilise un format de nom plus complexe. Voici un exemple:

http://en.wikipedia.org/wiki/Name_mangling

int example(int alpha, char beta);

en C: _example

en C++: __Z7exampleic

Mise à jour: Comme le note GManNickG dans les commentaires, le modèle de gestion des noms dépend du compilateur.

3
Harvey

extern "C", est un mot clé pour déclarer une fonction avec des liaisons C, car le compilateur C et le compilateur C++ traduisent le code source sous une forme différente dans un fichier objet:

Par exemple, un extrait de code est le suivant:

int _cdecl func1(void) {return 0}
int _stdcall func2(int) {return 0}
int _fastcall func3(void) {return 1}

Les compilateurs C 32 bits traduisent le code sous la forme comme suit:

_func1
_func2@4
@func3@4

dans le cdecl, func1 se traduira par ' _name '

dans l'appel std, func2 se traduira par ' _nom @ X '

dans l'appel rapide, func2 se traduira par ' @ name @ X '

'X' signifie le nombre d'octets des paramètres dans la liste de paramètres.

La convention 64 bits sous Windows ne comporte pas de trait de soulignement

En C++, les classes, les modèles, les espaces de noms et la surcharge d'opérateurs sont introduits, car les deux fonctions portant le même nom ne sont pas autorisées. Le compilateur C++ fournit les informations de type dans le nom du symbole, 

par exemple, un extrait de code est le suivant:

int func(void) {return 1;}
int func(int) {return 0;}
int func_call(void) {int m=func(), n=func(0);}

Le compilateur C++ traduira le code comme suit:

int func_v(void) {return 1;}
int func_i(int) {return 0;}
int func_call(void) {int m=_func_v(), n=_func_i(0);}

'_v' et '_i' sont des informations de type de 'void' et 'int'

0
Dongwei Wang