C'est probablement une réponse plus détaillée que vous ne le souhaitiez, mais je pense qu'une explication décente est justifiée.
En C et C ++, un fichier source est défini comme une unité de traduction . Par convention, les fichiers d'en-tête contiennent des déclarations de fonction, des définitions de type et des définitions de classe. Les implémentations de fonction réelles résident dans des unités de traduction, c'est-à-dire des fichiers .cpp.
L'idée derrière cela est que les fonctions et les fonctions membres de classe / structure sont compilées et assemblées une fois, puis d'autres fonctions peuvent appeler ce code à partir d'un endroit sans faire de doublons. Vos fonctions sont déclarées implicitement comme "extern".
/* Function declaration, usually found in headers. */
/* Implicitly 'extern', i.e the symbol is visible everywhere, not just locally.*/
int add(int, int);
/* function body, or function definition. */
int add(int a, int b)
{
return a + b;
}
Si vous souhaitez qu'une fonction soit locale pour une unité de traduction, vous la définissez comme «statique». Qu'est-ce que ça veut dire? Cela signifie que si vous incluez des fichiers source avec des fonctions externes, vous obtiendrez des erreurs de redéfinition, car le compilateur rencontre plusieurs fois la même implémentation. Donc, vous voulez que toutes vos unités de traduction voient la déclaration de la fonction mais pas le corps de la fonction .
Alors, comment tout est-il écrasé à la fin? C'est le travail de l'éditeur de liens. Un éditeur de liens lit tous les fichiers objets générés par l'étape assembleur et résout les symboles. Comme je l'ai dit plus tôt, un symbole n'est qu'un nom. Par exemple, le nom d'une variable ou d'une fonction. Lorsque les unités de traduction qui appellent des fonctions ou déclarent des types ne connaissent pas l'implémentation de ces fonctions ou types, ces symboles sont dits non résolus. L'éditeur de liens résout le symbole non résolu en connectant l'unité de traduction qui contient le symbole non défini avec celle qui contient l'implémentation. Phew. Cela est vrai pour tous les symboles visibles de l'extérieur, qu'ils soient implémentés dans votre code ou fournis par une bibliothèque supplémentaire. Une bibliothèque n'est en réalité qu'une archive avec du code réutilisable.
Il existe deux exceptions notables. Premièrement, si vous avez une petite fonction, vous pouvez la créer en ligne. Cela signifie que le code machine généré ne génère pas d'appel de fonction externe, mais est littéralement concaténé sur place. Puisqu'ils sont généralement petits, la taille des frais généraux n'a pas d'importance. Vous pouvez les imaginer statiques dans leur fonctionnement. Il est donc sûr d'implémenter des fonctions en ligne dans les en-têtes. Les implémentations de fonction dans une définition de classe ou de structure sont également souvent intégrées automatiquement par le compilateur.
L'autre exception concerne les modèles. Puisque le compilateur a besoin de voir toute la définition du type de modèle lors de leur instanciation, il n'est pas possible de découpler l'implémentation de la définition comme avec les fonctions autonomes ou les classes normales. Eh bien, c'est peut-être possible maintenant, mais obtenir un support généralisé du compilateur pour le mot-clé "export" a pris très, très longtemps. Ainsi, sans prise en charge de l '«exportation», les unités de traduction obtiennent leurs propres copies locales de types et de fonctions modèles instanciés, de la même façon que les fonctions en ligne fonctionnent. Avec la prise en charge de «l'exportation», ce n'est pas le cas.
Pour les deux exceptions, certaines personnes trouvent "plus agréable" de mettre les implémentations de fonctions en ligne, de fonctions basées sur des modèles et de types de modèles dans des fichiers .cpp, puis #incluez le fichier .cpp. Que ce soit un en-tête ou un fichier source n'a pas vraiment d'importance; le préprocesseur ne s'en soucie pas et n'est qu'une convention.
Un résumé rapide de l'ensemble du processus depuis le code C ++ (plusieurs fichiers) jusqu'à un exécutable final:
- Le préprocesseur est exécuté, qui analyse toutes les directives commençant par un '#'. La directive #include concatène le fichier inclus avec inferior, par exemple. Il effectue également le remplacement de macro et le collage de jetons.
- Le compilateur réel s'exécute sur le fichier texte intermédiaire après l'étape du préprocesseur et émet du code assembleur.
- L' assembleur s'exécute sur le fichier d'assemblage et émet du code machine, cela s'appelle généralement un fichier objet et suit le format exécutable binaire du système d'exploitation en question. Par exemple, Windows utilise le PE (format exécutable portable), tandis que Linux utilise le format ELF Unix System V, avec des extensions GNU. À ce stade, les symboles sont toujours marqués comme non définis.
- Enfin, l' éditeur de liens est exécuté. Toutes les étapes précédentes ont été exécutées sur chaque unité de traduction dans l'ordre. Cependant, l'étape de l'éditeur de liens fonctionne sur tous les fichiers objets générés qui ont été générés par l'assembleur. L'éditeur de liens résout les symboles et fait beaucoup de magie comme la création de sections et de segments, qui dépend de la plate-forme cible et du format binaire. Les programmeurs ne sont pas tenus de le savoir en général, mais cela aide sûrement dans certains cas.
Encore une fois, c'était certainement plus que ce que vous aviez demandé, mais j'espère que les détails concrets vous aideront à avoir une vue d'ensemble.