如何使用带有头文件和 .so 文件的库?

13

我刚开始学习C语言,想要使用一个库(MLT Multimedia Framework)。

我已经编译好了库,并生成了以下目录:include, lib, share

lib目录下有.so,.a, .la文件
include目录下有.h文件

现在我被指导这么做:

#include <framework/mlt.h> 它位于 include/mlt/framework/

问题:

  • 为什么需要包含只包含函数原型的头文件?真正的函数放在哪里?它们与lib目录中的函数有关联吗?
  • 我的文件应该放在哪里并且如何编译它?
  • 如何学习更多关于以下主题:
    • 动态/静态库
    • 构建/制作/安装
    • 如何使用任何的C库

1
给出具体的指示:你正在使用什么IDE/编译器等?纯GCC?一些图形化IDE?还是Makefiles? - deviantfan
1
什么是与C ++的关系?在C中使用C ++库有些棘手,甚至是不可能的。 - Ulrich Eckhardt
1
@Hi-Angel,C++库是可能的(但与其他平台/工具链等不太兼容)。而UlrichEckhardt在第一次提到的意思是:你不能在C中使用C++库。 - deviantfan
@UlrichEckhardt 嗯,因为C或C++中每个库都没有被编码过的名称,所以使用C?☺ - Hi-Angel
@UlrichEckhardt 它与C++的关系与C的关系相同。因此,“这与C++有什么关系”和“这与C有什么关系”是完全相等的问题。 - Hi-Angel
显示剩余8条评论
3个回答

25
如果没有函数原型,编译器怎么知道库中存在哪些函数呢?简短的答案是:它不知道。更长的答案是:编译器不关心库文件,无论是静态文件(以.a结尾)还是共享文件(以.so结尾),它只关心当前的翻译单元。解决未定义引用的问题是链接器的责任。当您使用库时,您需要将包含所需声明(结构、类、类型、函数原型)的头文件包含到源文件中。源文件加上所有包含的头文件形成编译器用于生成代码的翻译单元。如果有未定义的引用(例如对库中函数的调用),编译器会向生成的目标文件添加特殊信息。然后,链接器会查找所有目标文件,如果发现未解析的引用,则尝试在其他目标文件和提供的库中查找。如果所有定义都已解析,则链接器会生成最终可执行文件;否则,它将报告未解析的定义为错误。

回答你的其他问题:

我的文件应该放在哪里,如何编译它?

这是两个问题,第一个问题的答案(关于文件位置的问题)是并不重要。对于只有几个源文件和头文件的小项目,通常将所有文件放在一个共同的项目目录中。

关于编译的第二个问题,也有不同的方法。如果只有一个或两个源文件,可以使用编译器前端(例如 gcc)来编译和链接,并一步生成可执行文件:

$ gcc -Wall -g source1.c source2.c -o your_program_name

上述命令需要两个源文件,将它们编译并链接到程序your_program_name中。
如果您需要使用库,则需要在以上命令行中添加一两个内容:
  1. 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.

  2. 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会为你处理所有这些。它还将具有项目设置对话框,您可以在其中输入包含路径和库路径以及要链接的库。


1
哇?这是一个惊人的答案!易如反掌。 感谢您抽出时间解释这个问题。请写一本书吧! - Naughty.Coder

5

为什么我需要放置仅包含函数原型的头文件?

这样可以满足编译器对这些函数或类的声明。由于C++是静态类型检查语言,编译器必须知道它们将要使用的对象的类型。

我的文件应该放在哪里并如何编译?

您可以将代码放在文件系统中的任何位置;只需确保在编译时在包含路径和库中包含.h文件。通常需要修改路径。

您可以在此链接中了解有关构建的信息:

https://en.wikipedia.org/wiki/GNU_build_system


好的,但如果我只提供了头文件,真正的函数在哪里呢? - Naughty.Coder
它们将被实现在共享对象(.so)文件或静态库(.a)文件中。您可以检查lib文件夹以获取这些文件。 - mystic_coder

0

请查看随代码一起提供的 README 文件。它应该告诉您如何正确地将其安装到系统中。通常有一个名为 install 的构建目标,可以将生成的文件安装到正确的目录中。

大多数产品的构建和安装通常遵循以下命令序列:

$ ./configure
$ make
$ sudo make install

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接