web-dev-qa-db-fra.com

Mot clé externe C ++ sur les fonctions. Pourquoi ne pas simplement inclure le fichier d'en-tête?

Si je comprends bien, cela signifie

extern void foo();

que la fonction foo est déclarée dans une autre unité de traduction.

1) Pourquoi ne pas simplement inclure # l'en-tête dans lequel cette fonction est déclarée?

2) Comment l'éditeur de liens sait-il où chercher la fonction au moment de la liaison?

edit: Je devrais peut-être préciser que la déclaration ci-dessus est ensuite suivie en utilisant la fonction

foo();

Il n'est jamais défini dans cette unité de traduction.

43
user199421

1) Il peut ne pas avoir de fichier d'en-tête. Mais oui, en général, pour les grands projets, vous devriez avoir un fichier d'en-tête si plusieurs unités de traduction vont utiliser cette fonction (ne vous répétez pas).

2) L'éditeur de liens recherche dans tous les fichiers et bibliothèques d'objets dont il a été informé pour trouver des fonctions et d'autres symboles.

27
Brian Neal

Comme d'autres l'ont déjà dit, le mot clé extern est utilisé pour indiquer que le nom (une variable ou une fonction) a un lien externe, ce qui signifie que le nom fait référence au même objet dans tout le programme. De plus, c'est la valeur par défaut pour les variables et les fonctions définies au niveau du fichier, donc cette utilisation est superflue.

Il existe une autre utilisation du mot clé extern qui se présente comme suit:

extern "C" void foo();

Cela signifie que la fonction foo sera liée en utilisant les conventions C pour la liaison (peut-être parce que c'est une fonction définie dans une bibliothèque C ou est une fonction destinée à être appelée par des programmes C).

15
goedson

Non, cela signifie que la fonction foo est déclarée avec liaison externe. La liaison externe signifie que le nom foo fait référence à la même fonction dans tout le programme. L'endroit où la fonction est définie n'a pas d'importance. Il peut être défini dans cette unité de traduction. Il peut être défini dans une autre unité de traduction.

L'utilisation du mot clé extern comme indiqué dans votre exemple est superflue. Les fonctions ont toujours une liaison externe par défaut. Ce qui précède est 100% équivalent à juste

void foo();

Quant à l'éditeur de liens, lorsque l'éditeur de liens relie le programme, il ressemble simplement à partout. Il examine toutes les définitions jusqu'à ce qu'il trouve la définition de foo.

15
AnT

Cela signifie déjà que sans le mot clé extern. Les fonctions ont une liaison externe par défaut, sauf si vous les déclarez statiques.

L'utilisation de prototypes de fonction est acceptable, mais il est facile de se tromper. L'erreur de l'éditeur de liens que vous obtiendrez n'est pas si facile à diagnostiquer lorsque vous redéfinissez l'implémentation de la fonction. L'éditeur de liens ne sait pas où chercher, c'est votre travail de lui donner un fichier objet qui contient la définition de la fonction pour le garder heureux.

8
Hans Passant

1) Je ne sais pas pourquoi j'aurais besoin de cela pour une fonction. Peut-être que quelqu'un d'autre peut intervenir.

2) L'éditeur de liens détermine cela en parcourant tous les fichiers objets et en vérifiant les symboles à l'intérieur de chaque fichier objet. Je suppose que selon votre éditeur de liens, l'ordre de recherche exact peut varier.

Pour GNU binutils 'ld, tous les fichiers et bibliothèques d'objets qui apparaissent dans la ligne de commande de l'éditeur de liens après que l'objet contenant le symbole manquant soit recherché de gauche à droite et que le premier symbole trouvé soit choisi.

Exemple 1:

  • a.o - utilise foo (), bar ()
  • liba - fournit une barre ()
  • libb - fournit foo ()

$> ld a.o -la -lb

entraînera la recherche de symboles non définis dans a.o. Par la suite, ld parcourra les bibliothèques de gauche à droite pour rechercher ces symboles et trouvera bar dans liba et foo dans libb.

Cela peut entraîner des problèmes étranges sur les dépendances circulaires:

Exemple 2:

  • a.o - utilise la barre ()
  • liba - fournit bar (), utilise foo ()
  • libb - fournit foo (), utilise bar ()

Maintenant, il existe une dépendance circulaire entre liba et libb et la liaison échouera:

$> ld a.o -la -lb

car lors de la recherche dans les symboles non définis dans libb, ld déterminera qu'il n'y a pas d'autre lib à droite de -lb qui fournit ce symbole. Cela peut être résolu de deux manières au moins:

1) Lier la liba deux fois: $> ld a.o -la -lb -la

2) utilisez la fonction de regroupement de ld $> ld a.o --start-group -la -lb --end-group

Dans le cas 2), le regroupement indique à ld de rechercher dans tous les symboles de toutes les bibliothèques appartenant à ce groupe.

2
BjoernD