Включая заголовочный файл из статической библиотеки

Я делаю тестовую настройку статической библиотеки C и программы. Код библиотеки, расположенный в подкаталоге "foo" моего проекта, содержит следующие файлы:

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

Мой программный код выглядит следующим образом:

test.c:

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

У меня есть сценарий сборки, называемый "сборка", который содержит следующее:

построить:

#!/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

Но когда я запускаю сборку, она выдает мне следующую ошибку:

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

Однако он работает, когда я опускаю строку #include, но я бы предпочел использовать заголовочные файлы в моих статических библиотеках. Что я делаю не так, и как мне это исправить?

1 ответ

Решение

Заголовки не хранятся в библиотеках. Заголовки хранятся отдельно от библиотек. Библиотеки содержат объектные файлы; Заголовки не являются объектными файлами. По умолчанию стандартные заголовки в системе Unix хранятся в /usr/include - вы обычно найдете /usr/include/stdio.h а также /usr/include/string.h а также /usr/include/stdlib.h, например. По умолчанию библиотеки хранятся в /usr/lib (но вы также можете найти некоторые в /lib). Часто компиляторы также настроены на поиск в других местах. Одно общее альтернативное местоположение находится под /usr/local, так /usr/local/include для заголовков и /usr/local/lib для библиотек. Также обратите внимание, что в одной библиотеке может быть много заголовков, определяющих сервисы. Библиотека по умолчанию является примером. Он имеет функции, соответствующие тем, которые находятся в <stdio.h>, <string.h>, <stdlib.h> и много других заголовков тоже.

Глядя на ваш код:

  1. Если ваш заголовочный файл находится в ./foo/foo.hтогда вам нужно написать:

    #include "foo/foo.h"
    

    Или если вы продолжаете использовать #include "foo.h"вам нужно указать, где искать заголовок в командной строке компилятора с аргументом:

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

    Я сознательно исключил -static; это необходимо только тогда, когда есть выбор между статической и общей библиотекой, но у вас есть только libfoo.aтак что компоновщик будет использовать это в любом случае.

    Обратите внимание, что проблема заключается в ошибке компиляции, а не в ошибке компоновки. Это было бы понятнее, если бы вы разбили сборку программы на два этапа: (1) создать test.o и (2) ссылка на программу:

    gcc -c -Ifoo test.c
    gcc -o test test.o -L. -lfoo
    
  2. Ваш охранник заголовка неисправен. Первоначально вы имели (но обновили вопрос, так что эта опечатка больше не присутствует):

    #ifndef foo_h__
    #define foo_h_
    

    Тебе нужно:

    #ifndef foo_h__
    #define foo_h__
    

    Имена макросов должны быть одинаковыми в обеих строках. Обратите внимание, что в этом случае орфографические ошибки в основном безвредны, но в Mac OS X clang (маскируясь под gcc) предупредил об этом (хотя я заметил это до того, как сделал какую-либо компиляцию). В некоторых других случаях вы не получите защиту, на которую рассчитаны средства защиты заголовков.

    ./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.
    

Вы можете законно задуматься:

  • Если мне нужно -Ifoo при компиляции test.cпочему это не было необходимо при компиляции foo/foo.c?

Хороший вопрос!

  1. Это не повредило бы сборник foo/foo.c
  2. GCC ищет заголовки в каталоге, где находится исходный код модуля перевода (поэтому при компиляции foo/foo.cвыглядит в foo каталог для заголовков включен как #include "foo.h" тем не мение.
  3. Исходный файл foo/foo.c должен был включать foo.h тоже; это очень важно, так как компилятор обеспечивает перекрестную проверку, необходимую для обеспечения согласованности. Если бы вы написали #include "foo.h"компиляция будет работать как описано. Если вы написали (в foo/foo.c) #include "foo/foo.h"затем командная строка для создания foo.o понадобилось бы -I. так что заголовок можно найти.
Другие вопросы по тегам