Partage
  • Partager sur Facebook
  • Partager sur Twitter

Headers et fichiers objets

A quoi servent les headers si les fichiers objects existent ?

Sujet résolu
    19 mars 2022 à 15:44:19

    Bonjour à tous,


    J'ai une question qui me chagrine, excusez-moi si c'est un peu stupide ^^'

    Je souhaiterais savoir à quoi servent les headers ? Parce que les fichiers objets existent et les définitions des fonctions s'y trouvent dedans donc pourquoi avoir inventé les headers ? Je veux dire, ont-ils une réelle utilité dans la compilation, est-ce que en théorie on pourrait s'en passer ?

    Le compilateur ne peut-il pas directement aller chercher les définitions dans les fichiers objets/bibliothèques ?

    Aussi, la localisation des bibliothèques se trouvent-elles dans les headers pour ce qui est des fonctions standards du langage C ?

    Merci beaucoup et bonne journée

    • Partager sur Facebook
    • Partager sur Twitter
      19 mars 2022 à 16:28:49

      Bonjour,

      Un header est un fichier qui contient des déclarations de fonctions (mais pas les fonctions elles-mêmes)  et les macros éventuelles

      à partager entre plusieurs fichiers sources

      Les fonctions utilisées dans un source doivent avoir leur prototype dans celui-ci

      On écrit donc ceci, par exemple, en tête de source : #include <stdio.h>

      Le # signifie que cette instruction n'est pas une instruction C compilable mais une directive préprocesseur, celui-ci lit d'abord le contenu du fichier .h 

      avant de compiler le reste du source

      Ca revient à écrire le contenu du fichier .h en début de source en fait

      exemple de fichier header:

      /*
      	Checkers.h
      */
      
      // test evitant les inclusions multiples
      #ifndef included_checkers_h
      #define included_checkers_h
      
      // exemple de macro
      #define INFINITY	30000
      
      //exemples de fonctions "in line"
      #define MAX(v1,v2)	((v1 > v2)? v1:v2)
      #define MIN(v1,v2)	((v1 < v2)? v1:v2)
      
      // définition d'une structure utile dans plusieurs fichiers du projet
      typedef struct move_s
      {
      	char type;
      	short r1, c1, r2, c2;
      	short *successors;
      }MOVE_T;
      
      // exemples de prototypes de fonctions appelées dans les codes sources
      short minimax(char[][10], short, short*, short*);
      int make_moves(char [][10],short,short,short*);
      
      #endif



      -
      Edité par Phil_1857 19 mars 2022 à 17:53:00

      • Partager sur Facebook
      • Partager sur Twitter
        19 mars 2022 à 18:46:29

        ThéoLaurent6 a écrit:

        Bonjour à tous,


        J'ai une question qui me chagrine, excusez-moi si c'est un peu stupide ^^'

        Je souhaiterais savoir à quoi servent les headers ? Parce que les fichiers objets existent et les définitions des fonctions s'y trouvent dedans donc pourquoi avoir inventé les headers ? Je veux dire, ont-ils une réelle utilité dans la compilation, est-ce que en théorie on pourrait s'en passer ?

        [...]

        Parce que, comme te l'explique Phil, il y a une différence en C entre une définition et une déclaration. En très gros pour les fonctions, une déclaration dit comment utiliser une fonction (le type du retour, le nombre et le type des paramètres, son linkage, et parfois d'autres attributs suivant les plateformes et les compilos) ; une définition est le code de la fonction.

        Pour utiliser une fonction tu n'as besoin que de savoir comment l'utiliser, tu n'as pas besoin de savoir comment elle fonctionne. Du coup, en C, tu as besoin d'un endroit où tu peux trouver des déclarations de fonctions : ce sont les fichiers header. Comme la libc, la bibliothèque standard du C, contient une foultitude de fonctions ou les a regroupées plus ou moins par catégories : c'est pour cela qu'il y a plusieurs headers pour une seule (ou deux si on compte la libm le cas échéant) bibliothèque.

        ThéoLaurent6 a écrit:

        [...]
        Le compilateur ne peut-il pas directement aller chercher les définitions dans les fichiers objets/bibliothèques ?

        [...]

        En C, avec les ABI classiques ce n'est pas possible. Le code compilé que tu retrouves ou dans les bibliothèques ou les fichiers objets ou d'autres format ne permet pas de recréer simplement une déclaration d'une définition.

        Il ne faut pas oublier non plus que le terme «compilo» est un abus de langage. L'outil que tu utilises permet de non seulement de compiler un source en fichier objet (après une première phase utilisant le préprocesseur, traditionnellement  une «traduction en assembleur» puis un assemblage ; mais actuellement le processus est un poil plus complexe en ce qui concerne la représentation intermédiaire) ; mais il permet également de lier entre eux plusieurs fichiers objets pour en faire un binaire (exécutable ou bibliothèque en général). La phase de «compilation» à proprement parler n'a pas à essayer de chercher quoi que ce soit dans un binaire externe.

        De plus tu pourrais très bien avoir plusieurs versions concurrente d'un même bibliothèque … dans laquelle devrait-il chercher ?

        ThéoLaurent6 a écrit:

        [...]
        Aussi, la localisation des bibliothèques se trouvent-elles dans les headers pour ce qui est des fonctions standards du langage C ?

        Merci beaucoup et bonne journée


        Non. C'est à l'OS, et au linker de gérer ça. Tout ce dont tu as besoin ce sont des déclarations, d'où les headers.

        Le linker sait où trouver les bibliothèque, quitte à le lui indiquer au moment de l'édition des liens.

        L'OS sait où trouver quelle bibliothèque dans telle version/ABI/plateforme et la charger au moment voulu lors de l'exécution.

        • Partager sur Facebook
        • Partager sur Twitter
          19 mars 2022 à 18:53:08

          Merci pour votre réponse. En fait j'ai pas compris un truc.

          hello.c

          #include <stdio.h>
          
          void hello(void)
          {
             printf("hello\n");
          }

          main.c

          #include "hello.h"
          
          int main(void)
          {
             hello();
             return(0);
          }

          Dans une telle configuration, comment ça se passe à la compilation vu que main() appelle une fonction externe à son fichier source ?

          Est-ce que hello() remplacé par un genre d'instruction qui dit à l'ordinateur de chercher la fonction _hello dans le fichier objet hello.o ?

          Je suis un peu perdu sur ce niveau la...

          -------------------------------------------------------

          Merci beaucoup maintenant je comprend pourquoi je dois parfois préciser la lib à utiliser lors de la commande de compilation pour des bibliothèques externes, sinon le compilateur ne saurait pas où trouver leur définition.

          Le headers contenant la déclaration d'une fonction sert à vérifier que d'une part la fonction existe (donc que le linker sera bien en capacité de retrouver la fonction dans la lib) et de l'autre que les paramètres transmis sont correctes ? Ce qui signifie que les fichiers lib (contenant la définition de la fonction en binaire) eux ne permettent pas de vérifier la conformité des paramètres ?

          -
          Edité par ThéoLaurent6 19 mars 2022 à 19:10:49

          • Partager sur Facebook
          • Partager sur Twitter
            19 mars 2022 à 19:19:04

            ThéoLaurent6 a écrit:

            [...]
            Dans une telle configuration, comment ça se passe à la compilation vu que main() appelle une fonction externe à son fichier source ?

            Est-ce que l'instruction hello() est remplacé par le code de hello() ?

            Est-ce que l'instruction hello() est remplacé par une adresse vers le fichier hello.o ?

            Ou alors c'est remplacé par un genre d'instruction qui dit à l'ordinateur de chercher la fonction _hello dans le fichier objet hello.o ?

            Je suis un peu perdu sur ce niveau la...

            [...]

            Les différentes étapes pour hello.c

            • préprocesseur : phase qui ne concerne que la «traduction» de toutes les lignes qui commencent par #, vire tous les commentaires, etc.
            • compilation : phase qui traduit le code C en assembleur; La fonction main produite ressemblera à :

              main:
              .LFB0:
              	.cfi_startproc
              	pushq	%rbp
              	.cfi_def_cfa_offset 16
              	.cfi_offset 6, -16
              	movq	%rsp, %rbp
              	.cfi_def_cfa_register 6
              	call	hello@PLT
              	movl	$0, %eax
              	popq	%rbp
              	.cfi_def_cfa 7, 8
              	ret
              	.cfi_endproc
              
              Ici tu vois que le code dit simplement «il y a une fonction ommée hello qu'il faut appeler» ;
            • la phase de lien qui va prendre hello.o et main.o voir que dans main on a un appel à hello et que dans hello on a du code qui correspond à cette fonction … on fait le lien (d'où le nom linker, éditeur de liens).

            ThéoLaurent6 a écrit:

            [...]
            Le headers contenant la déclaration d'une fonction sert à vérifier que d'une part la fonction existe (donc que le linker sera bien en capacité de retrouver la fonction dans la lib) et de l'autre que les paramètres transmis sont correctes ? Ce qui signifie que les fichiers lib (contenant la définition de la fonction) eux ne permettent pas de vérifier la conformité des paramètres ?

            -
            Edité par ThéoLaurent6 il y a moins de 5s

            Le headers, en C, ne permet que décrire comment appeler une fonction. Il n'y a aucun lien quelconque vers une bibliothèque … on dit juste au compilo qu'une fonction existera quelque part et comment on l'utilise. Si au moment de l'édition des liens il ne la trouve pas alors on se prend un message d'erreur ; parfois c'est au runtime dans le cas d'une édition des liens dynamique.

            • Partager sur Facebook
            • Partager sur Twitter
              20 mars 2022 à 1:24:22

              Parfait merci à vous.

              Vous auriez des ressources à me recommander pour voir tout ça un peu plus en détail ?

              -
              Edité par ThéoLaurent6 20 mars 2022 à 1:30:36

              • Partager sur Facebook
              • Partager sur Twitter
                20 mars 2022 à 6:32:01

                Le contexte historique explique de nombreux choix pour le langage C.

                Dans un source, mettre une directive qui fait inclure du texte pris dans un autre fichier, ca existait déjà en Cobol (1961) et probablement dans tous les assembleurs, sur les systèmes qui avaient un disque.

                Le langage C était destiné à une machine avec quelques KILO octets ( il faut 1 mille kilos pour faire un mega, et un million pour faire un giga), et une vitesse d'exécution en dizaines de milliers d'instructions par seconde.

                Avec ces contraintes, il était nécessaire de simplifier au maximum le travail du compilateur, pour avoir

                • Des compilations en une seule passe
                • La compilation séparée (Source en plusieurs morceaux, compilés indépendamment en modules objets / bibliothèques) combinés ensuite pour faire un exécutable.

                Ça implique, lorsqu'on compile un appel de fonction, de connaître les paramètres qu'elle attend, avec une description de leur type.

                Parce qu'on ne compilera pas truc(12) de la même façon si le paramètre attendu est un entier, un char ou un flottant.

                Donc que tout appel soit fait dans un contexte où on a déjà rencontré les déclarations.

                Et pour éviter de redéclarer, inclusion d'un fichier header qui les contient.

                --

                Le mécanisme des #include permet ça. Et d'autre choses (inclusion de ce qu'on veut). Ce n'est pas la seule solution possible, mais les autres ne respectent pas les contraintes historiques.

                Aller voir dans les modules objets déjà compilés (comme en Java) ca implique de compiler en plusieurs fois et de gérer les dépendances. Pas faisable dans le contexte où C a été développé.

                -
                Edité par michelbillaud 20 mars 2022 à 6:38:11

                • Partager sur Facebook
                • Partager sur Twitter
                  20 mars 2022 à 8:52:03

                  ThéoLaurent6 a écrit:

                  Parfait merci à vous.

                  Vous auriez des ressources à me recommander pour voir tout ça un peu plus en détail ?

                  -
                  Edité par ThéoLaurent6 il y a environ 7 heures


                  N'importe quel cours C, de préférence un autre que cette ǝqnɐp que tu trouveras sur ce site. Certains supports décrivent en détail ce qui se passe pour passer d'un code à un exécutable = les différentes phases de compilation.

                  Il y a par exemple Effective C: An Introduction to Professional C Programming (chapitre 9), mais aussi Modern C (chapitre 2), et pour comprendre comment se fait une édition dynamique, comment ça fonctionne sur Linux en mettant les mains dans le cambouis le vieux mais toujours d'actualité et très poussé How to write shared libraries de Drepper.

                  Tu pourras noter que les mécanismes d'inclusion de headers avec des include guards pourra être remplacé par la nouvelle directive pragma once implémentée par les suites de compilos récents comme clang et gcc.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    22 mars 2022 à 19:50:44

                    Très bien, merci pour tout, sujet résolu ;)
                    • Partager sur Facebook
                    • Partager sur Twitter

                    Headers et fichiers objets

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    • Editeur
                    • Markdown