如果没有函数原型,编译器怎么知道库中存在哪些函数呢?简短的答案是:它不知道。更长的答案是:编译器不关心库文件,无论是静态文件(以
.a
结尾)还是共享文件(以
.so
结尾),它只关心当前的
翻译单元。解决未定义引用的问题是链接器的责任。当您使用库时,您需要将包含所需声明(结构、类、类型、函数原型)的头文件包含到源文件中。源文件加上所有包含的头文件形成编译器用于生成代码的翻译单元。如果有未定义的引用(例如对库中函数的调用),编译器会向生成的
目标文件添加特殊信息。然后,链接器会查找所有目标文件,如果发现未解析的引用,则尝试在其他目标文件和提供的库中查找。如果所有定义都已解析,则链接器会生成最终可执行文件;否则,它将报告未解析的定义为错误。
回答你的其他问题:
我的文件应该放在哪里,如何编译它?
这是两个问题,第一个问题的答案(关于文件位置的问题)是并不重要。对于只有几个源文件和头文件的小项目,通常将所有文件放在一个共同的项目目录中。
关于编译的第二个问题,也有不同的方法。如果只有一个或两个源文件,可以使用编译器前端(例如 gcc
)来编译和链接,并一步生成可执行文件:
$ gcc -Wall -g source1.c source2.c -o your_program_name
上述命令需要两个源文件,将它们编译并链接到程序
your_program_name
中。
如果您需要使用库,则需要在以上命令行中添加一两个内容:
You need to tell the linker to link with the library, this is done with e.g. the -l
(lower case L) option:
$ gcc -Wall -g source1.c source2.c -o your_program_name -lthe_library
It's important to note that the_library
is the base name of the library. If the library file is named libthe_library.so
then only the_library
part is needed, the linker will add the other parts automatically.
If the library is not in a standard location, then you need to tell the compiler and linker where the library file are. This is done with the -I
(capital i) option to tell the preprocessor where the header files are, and the -L
(capital l) where the linker files are.
Something like
$ gcc -Wall -g -Ilocation/of/headers source1.c source2.c -o your_program_name -Llocation/of/libraries -lthe_library
如果您有多个源文件,通常会使用所谓的
makefiles来列出所有源文件、它们的依赖关系、编译器和链接器标志,并包含构建对象文件和链接最终程序的规则。这样的makefile可能如下所示。
CFLAGS = -Wall -g
LDFLAGS = -g
SOURCES = source1.c source2.c
OBJECTS = $(SOURCES:.c=.o)
TARGET = your_program_name
.PHONY: all
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(LD) $(LDFLAGS) $^ -o $@
%.o: %.c
$(CC) $(CFLAGS) $< -c -o $@
上面的 Makefile 应该与之前的命令行几乎相同。最大的区别在于,它更容易添加更多源文件、为特殊文件添加特殊规则,最重要的是,make 程序将处理依赖关系,因此如果一个源文件自上次构建以来没有被修改,则不会编译它。这一点将使具有许多源文件的大型项目在只修改了一个或几个源文件时更快地构建。
如何了解更多关于这些主题的内容[...]
通过访问您喜欢的搜索引擎,然后在那里搜索这些主题。我还建议查看例如维基百科。
当然,如果你使用集成开发环境(IDE),那么你就不需要从命令行编译,也不需要自己制作makefile,IDE会为你处理所有这些。它还将具有项目设置对话框,您可以在其中输入包含路径和库路径以及要链接的库。