我正在进行C静态库和程序的测试设置。 库代码位于子目录“foo”中。我的项目,包含以下文件:
富/ foo.c的:
#include <stdio.h>
void foo(void) {
printf("something");
}
富/ foo.h中:
#ifndef foo_h__
#define foo_h__
extern void foo(void);
#endif
我的编程代码如下:
test.c的:
#include "foo.h"
int main() {
foo();
return 0;
}
我有一个名为&#39; build&#39;的构建脚本,其中包含以下内容:
构建
#!/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
但是当我运行build时,它会给我以下错误:
test.c:1:17: fatal error: foo.h: No such file or directory
#include "foo.h"
^
Compilation terminated
但是,当我省略#include行时,它确实有效,但我更喜欢在静态库中使用头文件。我做错了什么,我该如何解决?
答案 0 :(得分:26)
标头不存储在库中。标题与库分开存储。库包含目标文件;标头不是目标文件。默认情况下,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>
以及许多其他标题中的功能相对应。
查看您的代码:
如果您的标头文件位于./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
你的头部防守有问题。你最初有(但已经更新了问题所以这个错字不再存在):
#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
时没有必要?好问题!
foo/foo.c
foo/foo.c
时,它会在foo
目录中查找包含为#include "foo.h"
的标题foo/foo.c
也应包含foo.h
;这是非常重要的,因为编译器提供了确保一致性所必需的交叉检查。如果您已编写#include "foo.h"
,则编译将按所述方式工作。如果你写了(foo/foo.c
)#include "foo/foo.h"
,那么创建foo.o
的命令行就需要-I.
,因此可以找到标题。