134 votes

Pourquoi avons-nous besoin d’extern « C » {#include <foo.h>} en C++ ?</foo.h>

Plus précisément :

  • Quand est-ce que nous devrions utiliser il ?
  • Ce qui se passe au niveau du compilateur/éditeur de liens qui nous oblige à l’utiliser ?
  • Comment en termes de compilation/lier cela résout les problèmes qui nous obligent à utiliser ?

121voto

duane Points 1351

Le C et le C++ sont superficiellement similaires, mais chacun compile dans un ensemble très différent de code. Lorsque vous incluez un fichier d'en-tête avec un compilateur C++, le compilateur s'attend à du code C++. Si, toutefois, il est un en-tête C, alors le compilateur s'attend à ce que les données contenues dans le fichier d'en-tête pour être compilé dans un certain format-le C++ 'ABI', ou 'Application Binary Interface", de sorte que l'éditeur de liens étouffe. C'est préférable de passer C++ de données à une fonction attend C données.

(Pour entrer dans le vraiment nitty-gritty, C++'ABI de manière générale, "mangles" les noms de leurs fonctions/méthodes, afin de l'appelant printf() sans essoufflement le prototype d'une fonction C, le C++ va en fait générer le code appelant _Zprintf, plus de la merde à la fin.)

Donc: utiliser extern "C" {...}; lorsque l'on inclut un en-tête c-c'est très simple. Sinon, vous aurez une erreur dans le code compilé, et le linker va s'étouffer. Pour la plupart des en-têtes, cependant, vous n'aurez même pas besoin de l' extern parce que la plupart des système C-têtes déjà en compte le fait qu'ils pourraient être inclus par le code C++ et déjà extern de leur code.

Cheers!

109voto

tgamblin Points 25755

extern "C" détermine la façon dont les symboles dans les objets générés fichier doit être nommé. Si une fonction est déclarée sans extern "C", le nom du symbole dans le fichier de l'objet va utiliser C++ name mangling. Voici un exemple.

Test donné.C comme suit:

void foo() { }

La compilation et la liste des symboles dans le fichier d'objet donne:

$ g++ -c test.C
$ nm test.o
0000000000000000 T _Z3foov
                 U __gxx_personality_v0

Les foo fonction est en fait appelé "_Z3foov". Cette chaîne contient des informations de type pour le type de retour et les paramètres, entre autres choses. Si vous à la place de l'écriture.C comme ceci:

extern "C" {
    void foo() { }
}

Ensuite, compilez et regarder des symboles:

$ g++ -c test.C
$ nm test.o
                 U __gxx_personality_v0
0000000000000000 T foo

Vous obtenez une liaison C. Le nom de "foo" de la fonction dans le fichier d'objet est "toto", et ce n'est pas la fantaisie type d'info qui vient du nom d'amputation.

En général, vous inclure un en-tête dans extern "C" {} si le code qui va avec il a été compilé avec un compilateur C, mais que vous essayez de l'appeler à partir de C++. Lorsque vous faites cela, vous avez dit au compilateur que toutes les déclarations dans l'en-tête va utiliser une liaison C. Lorsque vous liez votre code, votre .o fichiers contiennent des références à la "toto", pas "_Z3fooblah", qui espérons-le, correspond à tout ce qui est dans la bibliothèque que vous créez un lien contre.

Plus modernes, les bibliothèques de mettre des gardes autour de ces en-têtes de sorte que les symboles sont déclarées avec le droit de liaison. par exemple, dans beaucoup de la norme en-têtes, vous trouverez:

#ifdef __cplusplus
extern "C" {
#endif

... declarations ...

#ifdef __cplusplus
}
#endif

Cela permet de s'assurer que, lorsque le code C++ inclut l'en-tête, les symboles dans l'objet de votre fichier de correspondre à ce qui est dans la bibliothèque C de. Vous ne devriez avoir à mettre extern "C" {} autour de votre en-tête C si il est vieux et n'a pas ces gardes déjà.

22voto

Trent Points 5924

En C++, vous pouvez avoir différentes entités qui partagent un nom. Pour exemple, voici une liste de toutes les fonctions nommé foo:

  • A::foo()
  • B::foo()
  • C::foo(int)
  • C::foo(std::string)

Afin de se différencier entre eux, le C++ le compilateur va créer des noms uniques pour chacune d'elles dans un processus appelé nom d'amputation ou de la décoration. Les compilateurs C ne pas le faire. En outre, chaque compilateur C++ de le faire, c'est d'une manière différente.

extern "C" indique au compilateur C++ de ne pas effectuer n'importe quel nom-déformation sur le code à l'intérieur des accolades. Cela vous permet d'appeler les fonctions C à partir de C++.

14voto

1800 INFORMATION Points 55907

Cela a à voir avec la façon dont les différents compilateurs effectuent nom-calandrage. Un compilateur C++ sera mangle le nom d’un symbole exporté depuis le fichier d’en-tête d’une façon totalement différente que ne le ferait un compilateur C, alors quand vous essayez de lier, vous obtiendrez une erreur de l’éditeur de liens disant il y avait des symboles manquants.

Pour résoudre ce problème, nous dire le compilateur C++ pour fonctionner en mode « C », alors il exécute nom calandrage de la même façon que le compilateur C serait. Ayant fait, les erreurs de l’éditeur de liens ont été fixés.

11voto

tialaramex Points 2351

Le C et le C++ ont des règles différentes concernant les noms de symboles. Les symboles sont comment l'éditeur de liens sait que l'appel à la fonction "openBankAccount" dans un fichier de l'objet produit par le compilateur est une référence à cette fonction, vous avez appelé "openBankAccount" dans un autre fichier de l'objet produit à partir d'un autre fichier source par le même (ou compatible) compilateur. Cela vous permet d'effectuer un programme de plus d'un fichier source, ce qui est un soulagement lorsque l'on travaille sur un grand projet.

En C, la règle est très simple, les symboles sont tous dans un seul espace de nom, de toute façon. Par conséquent, l'entier "chaussettes" est stockée en tant que "chaussettes" et la fonction count_socks est stockée en tant que "count_socks".

Les Linkers ont été construits pour C et autres langages tels que le C avec cette simple symbole de la règle de nommage. Donc, les symboles dans l'éditeur de liens sont de simples chaînes de caractères.

Mais en C++, le langage permet d'avoir des espaces de noms, et de polymorphisme et de diverses autres choses qui sont en conflit avec une règle simple. Tous les six de vos fonctions polymorphes appelé "ajouter" besoin d'avoir différents symboles, ou le mauvais sera utilisé par d'autres fichiers de l'objet. Ceci est fait par "déformation" (c'est un terme technique) les noms de symboles.

Lors de la liaison de code C++ à C de bibliothèques ou de code, vous devez extern "C" ce qui est écrit en C, tels que les fichiers d'en-tête pour les bibliothèques C, de dire à votre compilateur C++ que ces symboles ne sont pas déformés, tandis que le reste de votre code C++, évidemment, doit être mutilé ou ça ne marchera pas.

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