J'ai eu une interview récemment et une question posée était de savoir à quoi sert le extern "C"
code C ++. J'ai répondu qu'il s'agissait d'utiliser les fonctions C dans le code C ++ car C n'utilise pas la manipulation de noms. On m'a demandé pourquoi C n'utilisait pas de dénigrement et pour être honnête, je ne pouvais pas répondre.
Je comprends que lorsque le compilateur C ++ compile des fonctions, il donne un nom spécial à la fonction principalement parce que nous pouvons avoir des fonctions surchargées du même nom en C ++ qui doivent être résolues au moment de la compilation. En C, le nom de la fonction restera le même, ou peut-être précédé d'un _.
Ma question est la suivante: qu'est-ce qui ne va pas avec le fait d'autoriser le compilateur C ++ à modifier également les fonctions C? J'aurais supposé que peu importe les noms que le compilateur leur donne. Nous appelons les fonctions de la même manière en C et C ++.
la source
extern "C"
dit de modifier le nom de la même manière que "le" compilateur C le ferait.Réponses:
Cela a été en quelque sorte répondu ci-dessus, mais je vais essayer de mettre les choses en contexte.
Tout d'abord, C est venu en premier. En tant que tel, ce que fait C est en quelque sorte la "valeur par défaut". Il ne modifie pas les noms parce que ce n'est tout simplement pas le cas. Un nom de fonction est un nom de fonction. Un global est un global, et ainsi de suite.
Puis le C ++ est arrivé. C ++ voulait pouvoir utiliser le même éditeur de liens que C, et pouvoir établir des liens avec du code écrit en C. Mais C ++ ne pouvait pas laisser le C "mangling" (ou, il en manque) tel quel. Consultez l'exemple suivant:
En C ++, ce sont des fonctions distinctes, avec des corps distincts. Si aucun d'entre eux n'est mutilé, les deux seront appelés "fonction" (ou "_function"), et l'éditeur de liens se plaindra de la redéfinition d'un symbole. La solution C ++ a consisté à transformer les types d'arguments dans le nom de la fonction. Ainsi, l'un est appelé
_function_int
et l'autre est appelé_function_void
(pas de schéma de mutilation réel) et la collision est évitée.Maintenant, il nous reste un problème. Si a
int function(int a)
été défini dans un module C, et que nous prenons simplement son en-tête (c'est-à-dire la déclaration) dans du code C ++ et l'utilisons, le compilateur générera une instruction à l'éditeur de liens à importer_function_int
. Lorsque la fonction a été définie, dans le module C, elle ne s'appelait pas ainsi. Ça s'appelait_function
. Cela provoquera une erreur de l'éditeur de liens.Pour éviter cette erreur, pendant la déclaration de la fonction, nous disons au compilateur qu'il s'agit d'une fonction conçue pour être liée ou compilée par un compilateur C:
Le compilateur C ++ sait maintenant importer
_function
plutôt que_function_int
, et tout va bien.la source
-std=c++11
, et en évitant d'utiliser quoi que ce soit en dehors de la norme. C'est la même chose que de déclarer une version Java (bien que les versions plus récentes de Java soient rétrocompatibles). Ce n'est pas la faute aux normes que les gens utilisent des extensions spécifiques au compilateur et du code dépendant de la plateforme. D'un autre côté, vous ne pouvez pas les blâmer, car il manque beaucoup de choses (en particulier IO, comme les sockets) dans la norme. Le comité semble rattraper lentement cela. Corrigez-moi si j'ai raté quelque chose.Ce n'est pas qu'ils "ne peuvent pas", ils ne le sont pas , en général.
Si vous voulez appeler une fonction dans une bibliothèque C appelée
foo(int x, const char *y)
, il ne sert à rien de laisser votre compilateur C ++ le transformer enfoo_I_cCP()
(ou autre chose, juste inventé un schéma de transformation sur place ici) simplement parce qu'il le peut.Ce nom ne résoudra pas, la fonction est en C et son nom ne dépend pas de sa liste de types d'arguments. Ainsi, le compilateur C ++ doit le savoir et marquer cette fonction comme étant C pour éviter de faire le mangling.
N'oubliez pas que ladite fonction C peut être dans une bibliothèque dont vous n'avez pas le code source, tout ce que vous avez est le binaire précompilé et l'en-tête. Donc, votre compilateur C ++ ne peut pas faire "c'est propre", il ne peut pas changer ce qui est dans la bibliothèque après tout.
la source
extern "C"
:)Ce ne seraient plus des fonctions C.
Une fonction n'est pas seulement une signature et une définition; le fonctionnement d'une fonction est largement déterminé par des facteurs tels que la convention d'appel. L '"Interface binaire d'application" spécifiée pour une utilisation sur votre plate-forme décrit comment les systèmes se parlent. L'ABI C ++ utilisé par votre système spécifie un schéma de modification des noms, de sorte que les programmes de ce système sachent comment appeler des fonctions dans des bibliothèques, etc.(Lisez l'ABI Itanium C ++ pour un excellent exemple. Vous verrez très vite pourquoi c'est nécessaire.)
Il en va de même pour le C ABI de votre système. Certaines ABI C ont en fait un schéma de transformation des noms (par exemple Visual Studio), donc il s'agit moins de "désactiver la manipulation de noms" et plus de passer de l'ABI C ++ à l'ABI C, pour certaines fonctions. Nous marquons les fonctions C comme étant des fonctions C, pour lesquelles l'ABI C (plutôt que l'ABI C ++) est pertinent. La déclaration doit correspondre à la définition (que ce soit dans le même projet ou dans une bibliothèque tierce), sinon la déclaration est inutile. Sans cela, votre système ne saura tout simplement pas comment localiser / invoquer ces fonctions.
Quant à savoir pourquoi les plates-formes ne définissent pas les ABI C et C ++ comme étant les mêmes et se débarrassent de ce «problème», c'est en partie historique - les ABI C d'origine n'étaient pas suffisants pour C ++, qui a des espaces de noms, des classes et une surcharge d'opérateurs, tout dont doivent d'une manière ou d'une autre être représentés dans le nom d'un symbole d'une manière conviviale pour l'ordinateur - mais on pourrait également affirmer que faire en sorte que les programmes C respectent maintenant le C ++ est injuste pour la communauté C, qui devrait supporter une situation beaucoup plus compliquée ABI juste pour le bien d'autres personnes qui veulent l'interopérabilité.
la source
+int(PI/3)
, mais avec un grain de sel: je serais très prudent de parler de "C ++ ABI" ... AFAIK, il y a des tentatives pour définir des ABI C ++, mais pas de véritables standards de facto / de jure - comme isocpp.org/files /papers/n4028.pdf déclare (et je suis tout à fait d'accord), citation, il est profondément ironique que C ++ ait toujours soutenu un moyen de publier une API avec un ABI binaire stable - en recourant au sous-ensemble C de C ++ via extern «C ». .C++ Itanium ABI
est juste ça - un ABI C ++ pour Itanium ... comme discuté sur stackoverflow.com/questions/7492180/c-abi-issues-listMSVC modifie en fait les noms C, bien que de manière simple. Il ajoute parfois
@4
ou un autre petit nombre. Cela concerne les conventions d'appel et la nécessité de nettoyer la pile.Donc, la prémisse est tout simplement imparfaite.
la source
_
?/Gd, /Gr, /Gv, /Gz
. (C'est-à-dire que la convention d'appel standard est ce qui est utilisé à moins qu'une déclaration de fonction spécifie explicitement une convention d'appel.). Vous pensez à__cdecl
laquelle est la convention d'appel standard par défaut.Il est très courant d'avoir des programmes qui sont partiellement écrits en C et partiellement écrits dans un autre langage (souvent en langage d'assemblage, mais parfois en Pascal, FORTRAN ou autre). Il est également courant que les programmes contiennent différents composants écrits par différentes personnes qui ne disposent pas du code source pour tout.
Sur la plupart des plates-formes, il existe une spécification - souvent appelée ABI [Application Binary Interface] qui décrit ce qu'un compilateur doit faire pour produire une fonction avec un nom particulier qui accepte des arguments de certains types particuliers et renvoie une valeur d'un type particulier. Dans certains cas, une ABI peut définir plus d'une "convention d'appel"; les compilateurs pour de tels systèmes fournissent souvent un moyen d'indiquer quelle convention d'appel doit être utilisée pour une fonction particulière. Par exemple, sur Macintosh, la plupart des routines Toolbox utilisent la convention d'appel Pascal, donc le prototype de quelque chose comme "LineTo" serait quelque chose comme:
Si tout le code d'un projet a été compilé à l'aide du même compilateur, peu importe le nom du compilateur exporté pour chaque fonction, mais dans de nombreuses situations, il sera nécessaire pour le code C d'appeler des fonctions qui ont été compilées à l'aide d'autres outils et ne peut pas être recompilé avec le compilateur actuel [et peut très bien ne pas être en C]. Être capable de définir le nom de l'éditeur de liens est donc essentiel à l'utilisation de telles fonctions.
la source
J'ajouterai une autre réponse, pour aborder certaines des discussions tangentielles qui ont eu lieu.
L'ABI C (interface binaire d'application) appelait à l'origine à passer des arguments sur la pile dans l'ordre inverse (c'est-à-dire poussé de droite à gauche), où l'appelant libère également le stockage de la pile. L'ABI moderne utilise en fait des registres pour passer des arguments, mais la plupart des considérations de déformation remontent à la transmission d'arguments de la pile d'origine.
L'ABI Pascal original, en revanche, poussait les arguments de gauche à droite, et l'appelé devait faire apparaître les arguments. L'ABI C original est supérieur à l'ABI Pascal original sur deux points importants. L'ordre d'envoi des arguments signifie que le décalage de pile du premier argument est toujours connu, autorisant les fonctions qui ont un nombre inconnu d'arguments, où les premiers arguments contrôlent le nombre d'autres arguments (ala
printf
).La deuxième façon dont l'ABI C est supérieur est le comportement au cas où l'appelant et l'appelé ne s'accordent pas sur le nombre d'arguments. Dans le cas C, tant que vous n'accédez pas réellement aux arguments après le dernier, rien de mal ne se passe. Dans Pascal, le nombre incorrect d'arguments est sorti de la pile et la pile entière est corrompue.
L'ABI Windows 3.1 d'origine était basé sur Pascal. En tant que tel, il a utilisé l'ABI Pascal (arguments dans l'ordre de gauche à droite, l'appelé apparaît). Étant donné que toute non-concordance dans le numéro d'argument peut entraîner une corruption de la pile, un schéma de mutilation a été formé. Chaque nom de fonction a été mutilé avec un nombre indiquant la taille, en octets, de ses arguments. Donc, sur une machine 16 bits, la fonction suivante (syntaxe C):
A été mutilé
function@2
, parce queint
fait deux octets de large. Cela a été fait pour que si la déclaration et la définition ne correspondent pas, l'éditeur de liens ne parviendra pas à trouver la fonction plutôt que de corrompre la pile au moment de l'exécution. Inversement, si le programme est lié, vous pouvez être sûr que le nombre correct d'octets est extrait de la pile à la fin de l'appel.Windows 32 bits et versions ultérieures utilisent le
stdcall
ABI à la place. Il est similaire à l'ABI Pascal, sauf que l'ordre de poussée est comme en C, de droite à gauche. Comme le Pascal ABI, le nom qui modifie la taille en octets des arguments dans le nom de la fonction pour éviter la corruption de la pile.Contrairement aux revendications faites ailleurs ici, l'ABI C ne modifie pas les noms des fonctions, même sur Visual Studio. Inversement, les fonctions de manipulation décorées avec la
stdcall
spécification ABI ne sont pas uniques à VS. GCC prend également en charge cette ABI, même lors de la compilation pour Linux. Ceci est largement utilisé par Wine , qui utilise son propre chargeur pour permettre la liaison au moment de l'exécution des binaires compilés Linux aux DLL compilées Windows.la source
Les compilateurs C ++ utilisent la gestion des noms afin de permettre des noms de symboles uniques pour les fonctions surchargées dont la signature serait autrement la même. Il encode également les types d'arguments, ce qui permet un polymorphisme au niveau des fonctions.
C ne l'exige pas car il ne permet pas de surcharger les fonctions.
Notez que le changement de nom est une des raisons (mais certainement pas la seule!) Pour laquelle on ne peut pas se fier à un 'C ++ ABI'.
la source
C ++ veut pouvoir interopérer avec le code C qui se lie à lui, ou avec lequel il est lié.
C attend des noms de fonction non mutilés par le nom.
Si C ++ le mutilait, il ne trouverait pas les fonctions non mutilées exportées à partir de C, ou C ne trouverait pas les fonctions C ++ exportées. L'éditeur de liens C doit obtenir le nom qu'il attend lui-même, car il ne sait pas qu'il vient ou va vers C ++.
la source
La gestion des noms des fonctions et des variables C permettrait de vérifier leurs types au moment de la liaison. Actuellement, toutes les implémentations (?) C vous permettent de définir une variable dans un fichier et de l'appeler en tant que fonction dans un autre. Ou vous pouvez déclarer une fonction avec une mauvaise signature (par exemple
void fopen(double)
, puis l'appeler.J'ai proposé un schéma pour la liaison de type sûr des variables et des fonctions C par l'utilisation de la mutilation en 1991. Le schéma n'a jamais été adopté, car, comme d'autres l'ont noté ici, cela détruirait la rétrocompatibilité.
la source