La liaison linguistique est le terme utilisé pour désigner la liaison entre C++
et non-C++
des fragments de code. Typiquement, dans un programme C++, tous les noms de fonctions, les types de fonctions et même les noms de variables ont le lien par défaut du langage C++.
Un code objet C++ peut être lié à un autre code objet produit à l'aide d'un autre langage source (tel que C
) en utilisant un spécificateur de lien prédéfini.
Comme vous devez le savoir, le concept de name mangling
qui encode les noms de fonctions, les types de fonctions et les noms de variables de manière à leur donner un nom unique. Cela permet à l'éditeur de liens de différencier les noms communs (comme dans le cas de la surcharge des fonctions). La gestion des noms n'est pas souhaitable lorsque l'on lie des modules C avec des bibliothèques ou des fichiers objets compilés avec un compilateur C++. Pour empêcher la manipulation des noms dans de tels cas, des spécificateurs de liaison sont utilisés. Dans ce cas, extern "C"
est le spécificateur de liaison. Prenons un exemple (code c++ mentionné ici ) :
typedef int (*pfun)(int); // line 1
extern "C" void foo(pfun); // line 2
extern "C" int g(int) // line 3
...
foo( g ); // Error! // line 5
La ligne 1 déclare pfun
pour pointer vers une fonction C++, car il lui manque un spécificateur de lien.
La ligne 2 déclare donc que foo est une fonction C qui prend un pointeur vers une fonction C++.
La ligne 5 tente d'appeler foo avec un pointeur vers g, une fonction C, une erreur de correspondance de type.
Diff dans la liaison des noms de fonctions :
Prenons deux fichiers différents :
Un avec extern "c"
liaison (file1.cpp) :
#include <iostream>
using namespace std;
extern "C"
{
void foo (int a, int b)
{
cout << "here";
}
}
int main ()
{
foo (10,20);
return 0;
}
Un sans extern "c"
liaison (file2.cpp) :
#include <iostream>
using namespace std;
void foo (int a, int b)
{
cout << "here";
}
int main ()
{
foo (10,20);
return 0;
}
Maintenant compilez ces deux-là et vérifiez l'objdump.
# g++ file1.cpp -o file1
# objdump -Dx file1
# g++ file2.cpp -o file2
# objdump -Dx file2
Avec la liaison extern "C", il n'y a pas de manipulation de nom pour la fonction foo
. Ainsi, tout programme qui l'utilise (en supposant que nous en fassions une librairie partagée) peut directement appeler foo (avec des fonctions d'aide telles que dlsym
et dlopen
) sans tenir compte des effets de la confusion des noms.
0000000000400774 <foo>:
400774: 55 push %rbp
400775: 48 89 e5 mov %rsp,%rbp
....
....
400791: c9 leaveq
400792: c3 retq
0000000000400793 <main>:
400793: 55 push %rbp
400794: 48 89 e5 mov %rsp,%rbp
400797: be 14 00 00 00 mov $0x14,%esi
40079c: bf 0a 00 00 00 mov $0xa,%edi
4007a1: e8 ce ff ff ff callq 400774 <foo>
4007a6: b8 00 00 00 00 mov $0x0,%eax
4007ab: c9 leaveq
D'autre part, lorsqu'aucun extern "C"
est utilisé, func : foo
est manipulé selon certaines règles prédéfinies (connues du compilateur/lien utilisé) et une application ne peut donc pas l'appeler directement à partir de celui-ci en spécifiant le nom en tant que foo
. Vous pouvez cependant l'appeler avec le nom tronqué ( _Z3fooii
dans ce cas) si vous le souhaitez, mais personne ne l'utilise pour la raison évidente.
0000000000400774 <_Z3fooii>:
400774: 55 push %rbp
400775: 48 89 e5 mov %rsp,%rbp
...
...
400791: c9 leaveq
400792: c3 retq
0000000000400793 <main>:
400793: 55 push %rbp
400794: 48 89 e5 mov %rsp,%rbp
400797: be 14 00 00 00 mov $0x14,%esi
40079c: bf 0a 00 00 00 mov $0xa,%edi
4007a1: e8 ce ff ff ff callq 400774 <_Z3fooii>
4007a6: b8 00 00 00 00 mov $0x0,%eax
4007ab: c9 leaveq
4007ac: c3 retq
Cette page est également une bonne lecture pour ce sujet particulier.
Un article agréable et clairement expliqué sur la convention d'appel : http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx