40 votes

Que signifie le fait qu'un nom ou un type ait un certain lien avec la langue ?

Selon (c) ANSI ISO/IEC 14882:2003, page 127 :

Les spécifications de liaison s'imbriquent. Lorsque les spécifications de liaison sont imbriquées, la spécification la plus interne détermine la langue. Une spécification de lien n'établit pas de portée. Une spécification de lien ne doit apparaître que dans la portée d'un espace de nom (3.3). Dans une spécification de lien, le lien de langage spécifié s'applique aux types de fonction de tous les déclarateurs de fonction, noms de fonction et noms de variable introduits par la ou les déclarations.

extern "C" void f1(void(*pf)(int));
// the name f1 and its function type have C language
// linkage; pf is a pointer to a C function

extern "C" typedef void FUNC();
FUNC f2;
// the name f2 has C++ language linkage and the
// function's type has C language linkage

extern "C" FUNC f3;
// the name of function f3 and the function's type
// have C language linkage

void (*pf2)(FUNC*);
// the name of the variable pf2 has C++ linkage and
// the type of pf2 is pointer to C++ function that
// takes one parameter of type pointer to C function

Que signifie tout cela ? Par exemple, quel est le lien entre le f2() ont-ils un lien avec le langage C ou C++ ?

Comme l'a souligné @Johannes Schaub, il n'y a pas de véritable explication de ce que cela signifie dans la norme, de sorte que cela peut être interprété différemment dans différents compilateurs.

Veuillez expliquer les différences dans le fichier objet :

  • le nom d'une fonction avec un lien en langage C et un lien en langage C++.
  • le type d'une fonction avec un lien en langage C et un lien en langage C++.

16voto

Vikram.exe Points 2602

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

2voto

Roee Gavirel Points 4550

"le nom f2 a un lien avec le langage C++" Dans le langage C++, non seulement le nom de la fonction la définit, mais aussi le type de ses arguments et la valeur de retour. Dans ce cas, vous avez : void f2(void) ; mais vous pouvez définir avec lui : void f2(int a) ; sans conflit car le linkage les verra comme des types différents, une chose que vous ne pourriez pas faire en langage C.

"le type de la fonction a un lien avec le langage C" Je ne connais pas les détails mais je connais le haut niveau de la chose. En gros, cela permet de lier une fonction compilée en C++ à partir du C. Si je me souviens bien, en C et en C++, la façon dont les paramètres sont passés à une fonction est différente. Dans ce cas, la fonction f2 passera les paramètres comme le compilateur C le fait. De cette façon, la fonction sera linkable à la fois en C et en C++.

2voto

Bo Persson Points 42821
extern "C" typedef void FUNC();
FUNC f2;
// the name f2 has C++ language linkage and the
// function's type has C language linkage

Le nom FUNC est déclaré avec un lien "C" car il dit extern "C" sur la première ligne.

Le nom f2 a un lien C++ parce que c'est le cas par défaut, et aucun autre lien n'est donné à la ligne 2.

Le fait que le nom f2 est utilisé pour faire référence à une fonction avec un lien C ne change pas le lien de la fonction nom .

2voto

Mehrdad Points 70493

Cela a à voir avec le ABI (Application Binary Interface) du programme.

En tant qu'API, il spécifie l'interface externe de l'application code source d'un programme, l'ABI spécifie l'interface externe du programme. code binaire du programme (la version compilée).


À l'origine, les fonctions C avaient simplement quelques formes différentes. Quelque chose comme

int foo(int);

serait préfixé par un trait de soulignement par le compilateur, pour former _foo et ensuite exporté pour être mis à la disposition d'autres applications.

Cependant, ce n'était pas suffisant. Si vous regardez l'API Windows, par exemple, vous verrez des choses comme :

DWORD CreateWindowW(...);        //Original parameters
DWORD CreateWindowExW(..., ...); //More parameters

En effet, il n'y a aucun moyen de distinguer les surcharges d'une fonction en regardant simplement le nom de la fonction. Ex (ou un suffixe similaire).

Ce système est devenu assez laid et ne permettait toujours pas de surcharger les opérateurs, ce qui était le cas en C++. Pour cette raison, le C++ a créé la confusion des noms pour mettre informations supplémentaires dans le nom de la fonction, comme les types de données de ses paramètres, et en faire quelque chose de cryptique avec beaucoup de @ des symboles.

Tout allait bien, sauf que il n'était pas complètement normalisé .

Bien sûr, au fur et à mesure que de nouveaux langages et compilateurs sont apparus, chacun a proposé son propre schéma, dont certains sont incompatibles avec d'autres. Ainsi, si vous avez besoin d'importer ou d'exporter une fonction externe, vous devez spécifier quel type d'ABI le compilateur doit rechercher, d'où l'utilisation de la fonction extern "C++" que vous avez là.

2voto

Tony D Points 43962

Que signifie tout cela ? Par exemple, quelle est la liaison de la fonction f2(), celle du langage C ou C++ ?

extern "C" typedef void FUNC();
FUNC f2;
// the name f2 has C++ language linkage and the 
// function's type has C language linkage 

Ce que vous appelez la "fonction f2()" a deux aspects dans sa liaison :

  • la manipulation ou non de son nom dans la table des symboles (qui a un lien avec le langage C++), et
  • la convention d'appel C ou C++ nécessaire si la fonction est appelée (C).

Pour appeler f2() vous trouverez son nom et son symbole dans le fichier objet, qui sera une version tronquée de "function named f2 taking no arguments". Vous pouvez le vérifier trivialement en compilant le code ci-dessus et en inspectant l'objet (par exemple avec les outils GNU). nm --demangle ).

Mais pour appeler la fonction, les conventions pour les pré et post-conditions concernant l'utilisation des registres, la configuration de la pile, etc. sont celles d'une fonction C. Il est légal pour les fonctions C et C++ d'avoir des conventions d'appel différentes, et cela peut être fait - par exemple - pour faciliter la gestion des exceptions C++.

Veuillez expliquer les différences dans le fichier objet : le nom d'une fonction avec une liaison en langage C et une liaison en langage C++.

  • pour une liaison C, "f2" serait le symbole dans le fichier objet résultant de f2()
  • pour le lien C++, une version tronquée de "function named f2 taking no arguments" (pour GNU, _Z2f2v qui démangent à f2() )

le type d'une fonction avec un lien en langage C et un lien en langage C++.

Comme nous l'avons vu plus haut, il s'agit de la convention d'utilisation des registres et de la pile pour appeler le code à l'adresse de la fonction. Cette méta-information n'est pas nécessairement stockée dans les informations de la table des symboles de l'objet (et ne fait certainement pas partie de la clé de nom de symbole elle-même).

De plus, comme chaque fonction adopte l'une des conventions d'appel, un compilateur doit savoir quelle convention d'appel utiliser lorsqu'il suit un pointeur vers une fonction : avec cet aperçu, je pense que le code restant dans la question devient clair.

Il y a une excellente discussion à http://developers.sun.com/solaris/articles/mixing.html - Je recommande en particulier la section Travailler avec des pointeurs vers des fonctions .

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