【问题标题】:Including header file from static library包括来自静态库的头文件
【发布时间】:2015-02-23 23:27:24
【问题描述】:

我正在对 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;
}

我有一个名为“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

但是当我运行构建时,它给了我以下错误:

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

但是,当我省略 #include 行时,它确实有效,但如果我可以在我的静态库中使用头文件,我会更喜欢。我做错了什么,我该如何解决?

【问题讨论】:

    标签: c gcc static-libraries header-files .a


    【解决方案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 用于库。还要注意,单个库可能有许多定义服务的标头。默认库就是一个例子。它具有与&lt;stdio.h&gt;&lt;string.h&gt;&lt;stdlib.h&gt; 和许多其他标头中的功能相对应的功能。

    查看您的代码:

    1. 如果你的头文件在./foo/foo.h,那么你需要写:

      #include "foo/foo.h"
      

      或者如果你继续使用#include "foo.h",你需要在编译器命令行中用参数指定哪里可以找到header:

      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.
      

    你可能想知道:

    • 如果我在编译test.c时需要-Ifoo,为什么编译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.,这样才能找到标题。

    【讨论】:

    • 但是头文件 foo.h 是库 libfoo.a 的一部分,不是吗?此外,当我在 stackoverflow 上重新键入标题保护问题时,我这样做是因为我在 windows 上通过 SSH 连接到 ubuntu 系统。
    • 没有。库仅包含目标文件。标头不是目标文件。标头不包含在库中。 (好吧:为了完全准确,您可以将头文件添加到库中,但编译器永远不会在库中查找 - 只有链接器(通常为ld)会在库中查找,并且只查找目标文件,而不是标题)。
    • 哦,好的。谢谢你澄清这一点。但是,例如,为什么我可以包含来自 ncurses 库的“ncurses.h”?
    • 您可以写#include &lt;ncurses.h&gt;,因为标头安装在标准位置/usr/include,编译器无论如何都会搜索标头。你可以写-lncurses,因为库位于链接器搜索的标准位置,通常是/usr/lib。或者它可能是/usr/local/include/usr/local/lib。但是标题不存储在库中;它们独立于库存储。您可以在没有标头的情况下安装库。然后您无法编译使用标头的代码,但您可以运行使用共享库的代码。
    • 如果我有 h 文件 a.h 并且我的 C 文件使用相对路径调用它:$include "../../SomeFolderA/SomeFolderB/a.h。现在头文件a.h 包含一个名为b.h 的h 文件,使用SomeFolderC\b.h。 GCC会正确解析它还是所有路径都相对于C文件路径解析?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多