web-dev-qa-db-fra.com

Inclusion du fichier d'en-tête de la bibliothèque statique

Je fais une configuration de test d'une bibliothèque statique C et d'un programme. Le code de la bibliothèque, situé dans un sous-répertoire 'foo' de mon projet, contient les fichiers suivants:

foo/foo.c:

#include <stdio.h>
void foo(void) {
    printf("something");
}

foo/foo.h:

#ifndef foo_h__
#define foo_h__
extern void foo(void);
#endif

Mon code de programme est le suivant:

test.c:

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

J'ai un script de build, appelé 'build', qui contient les éléments suivants:

build:

#!/bin/bash
gcc -c -Wall -Werror foo/foo.c
ar rcs libfoo.a foo.o
gcc -static -o test test.c libfoo.a # I have also tried -L. -lfoo

Mais lorsque j'exécute la génération, cela me donne l'erreur suivante:

test.c:1:17: fatal error: foo.h: No such file or directory
  #include "foo.h"
                  ^
Compilation terminated

Cependant, cela fonctionne lorsque j'omet la ligne #include, mais je préférerais pouvoir utiliser des fichiers d'en-tête dans mes bibliothèques statiques. Qu'est-ce que je fais mal et comment puis-je y remédier?

14
Ethan McTague

Les en-têtes ne sont pas stockés dans les bibliothèques. Les en-têtes sont stockés séparément des bibliothèques. Les bibliothèques contiennent des fichiers objets; les en-têtes ne sont pas des fichiers objets. Par défaut, les en-têtes standard sur un système Unix sont stockés dans /usr/include - vous trouverez normalement /usr/include/stdio.h et /usr/include/string.h et /usr/include/stdlib.h, par exemple. Par défaut, les bibliothèques sont stockées dans /usr/lib (mais vous pouvez également en trouver dans /lib). Souvent, les compilateurs sont configurés pour rechercher également dans d'autres endroits. Un autre emplacement courant se trouve sous /usr/local, alors /usr/local/include pour les en-têtes et /usr/local/lib pour les bibliothèques. Notez également qu'une seule bibliothèque peut avoir plusieurs en-têtes définissant les services. La bibliothèque par défaut est un exemple. Il a les fonctions correspondant à celles trouvées dans <stdio.h>, <string.h>, <stdlib.h> et de nombreux autres en-têtes également.

En regardant votre code:

  1. Si votre fichier d'en-tête est dans ./foo/foo.h, alors vous devez écrire:

    #include "foo/foo.h"
    

    Ou si vous continuez à utiliser #include "foo.h", vous devez spécifier où trouver l'en-tête sur la ligne de commande du compilateur avec l'argument:

    gcc -Ifoo -o test test.c -L. -lfoo
    

    J'ai délibérément exclu le -static; c'est seulement nécessaire quand il y a le choix entre une bibliothèque statique et une bibliothèque partagée, mais vous n'avez que libfoo.a, donc l'éditeur de liens l'utilisera quand même.

    Notez que le problème est une erreur de compilation, pas une erreur de liaison. Ce serait plus clair si vous divisez la construction du programme en deux étapes: (1) create test.o et (2) programme de liaison:

    gcc -c -Ifoo test.c
    gcc -o test test.o -L. -lfoo
    
  2. Votre protège-tête est défectueux. Vous l'aviez à l'origine (mais vous avez mis à jour la question de sorte que cette faute de frappe n'est plus présente):

    #ifndef foo_h__
    #define foo_h_
    

    Vous avez besoin:

    #ifndef foo_h__
    #define foo_h__
    

    Les noms de macro doivent être identiques sur les deux lignes. Notez que dans ce cas, la faute d'orthographe est principalement inoffensive - mais sur Mac OS X, clang (se faisant passer pour gcc) a donné un avertissement à ce sujet (bien que je l'ai repéré avant de le faire) toute compilation). Dans certains autres cas, vous n'obtiendrez pas la protection que les protecteurs d'en-tête sont conçus pour fournir.

    ./foo/foo.h:1:9: warning: 'foo_h__' is used as a header guard here, followed by #define of a
          different macro [-Wheader-guard]
    #ifndef foo_h__
            ^~~~~~~
    ./foo/foo.h:2:9: note: 'foo_h_' is defined here; did you mean 'foo_h__'?
    #define foo_h_
            ^~~~~~
            foo_h__
    1 warning generated.
    

Vous pourriez légitimement vous demander:

  • Si j'ai besoin de -Ifoo lors de la compilation test.c, pourquoi n'était-il pas nécessaire lors de la compilation foo/foo.c?

Bonne question!

  1. Cela n'aurait pas nui à la compilation de foo/foo.c
  2. GCC recherche les en-têtes dans le répertoire où se trouve le code source de l'unité de traduction (donc, lors de la compilation de foo/foo.c, il recherche dans le répertoire foo les en-têtes inclus comme #include "foo.h" en tous cas.
  3. Le fichier source foo/foo.c aurait dû inclure foo.h aussi; il est très important que ce soit le cas, car c'est ainsi que le compilateur fournit le recoupement nécessaire pour garantir la cohérence. Si vous aviez écrit #include "foo.h", la compilation fonctionnerait comme décrit. Si vous avez écrit (en foo/foo.c) #include "foo/foo.h", puis la ligne de commande pour créer foo.o aurait eu besoin de -I. pour que l'en-tête soit trouvé.
28
Jonathan Leffler