共享库和.h文件

16
我对程序如何使用共享库有一些疑问。 当我构建一个共享库(使用-shared -fPIC开关)时,我会使一些函数可以从外部程序中调用。 通常我会使用dlopen()加载库,然后使用dlsym()将这些函数链接到我的程序中的某些函数指针上。 这种方法不涉及包含任何.h文件。 是否有一种方法可以避免使用dlopen()dlsym(),而只需包含共享库的.h文件? 我猜这可能是C++程序使用存储在系统共享库中的代码的方式,即只需包含stdlib.h等文件。
5个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
73
尼克,我认为其他答案实际上回答了你的问题,那就是如何链接库,但你提出问题的方式表明你对头文件和库之间的区别有误解。它们不是相同的东西。你需要 两者,它们也没有做相同的事情。 构建可执行文件分为两个主要阶段,编译(将源代码转换为中间形式,包含可执行二进制指令,但不是可运行程序)和链接(将这些中间文件组合成一个单独的可运行的可执行文件或库)。 当你执行 gcc -c program.c 时,你正在进行编译,并生成 program.o。这一步是头文件很重要的地方。你需要在 program.c 中进行 #include <stdlib.h>,以便(例如)使用 mallocfree。(类似地,你需要为 dlopendlsym 进行 #include <dlfcn.h>)。如果你不这样做,编译器会抱怨它不知道这些名称是什么,并停止并报错。但如果你使用 #include 引入头文件,编译器并不会把你调用函数的代码插入到 program.o 中。它只是插入一个对它们的 引用。原因是避免代码重复:每个程序部分只需要访问这些代码一次,所以如果你需要更多文件(module1.cmodule2.c 等等),即使它们 全部 使用了 malloc,你最终只会得到指向单个 malloc 副本的多个引用。该副本存在于标准库中,以其共享或静态形式 (libc.solibc.a) 存在,但是它们不在源代码中被引用,编译器也不知道它们存在。链接器就是链接器。在链接阶段,你需要运行 gcc -o program program.o 命令。链接器会搜索你在命令行中传递给它的所有库,并查找你调用但未在自己代码中定义的所有函数的单个定义。这就是 -l 的作用(正如其他人所解释的那样):告诉链接器你需要使用的库列表。它们的名称通常与你在前一步中使用的头文件无关。例如,要使用 dlsym 函数,你需要 libdl.solibdl.a 库,因此你的命令行将是 gcc -o program program.o -ldl。要使用 malloc 或大多数 std*.h 头文件中的函数,你需要 libc 库,但由于该库被每个 C 程序使用,所以它会自动链接(就好像你已经执行了 -lc)。很抱歉如果我详细介绍了一些内容,但如果你不知道区别,你肯定会想知道的。如果你不知道 C 编译过程是如何运作的话,这将非常难以理解。最后,dlopendlsym 不是链接的常规方法。它们用于特殊情况,其中你想要基于某些原因只能在运行时才能获得的信息动态确定你想要的行为。如果你在编译时知道要调用哪些函数(在 99% 的情况下都是这样),你不需要使用 dl* 函数。

你写道:“例如,要使用dlsym,您需要libdl.so或libdl.a,因此您的命令行将是gcc -o program program.o -ldl。”我该如何告诉链接器它必须使用共享库还是静态库? - nick2k3
假设您正在使用gcc?链接器默认情况下会优先选择共享库而不是静态库。要使其优先选择静态库,请使用“-static”标志。 - quark
顺便提一下,如果有疑问:最好使用共享库。这对于系统库尤其如此。静态库确实有用处,但通常是特殊情况。 - quark

3

您可以像链接静态库一样链接共享库。 然后在启动程序时搜索它们。 实际上,默认情况下,-lXXX将优先选择libXXX.so而不是libXXX.a。


2
你需要向链接器提供正确的指令来链接你的共享库。 共享库的名称类似于libNAME.so,因此在链接时应该使用-lNAME。 将其命名为libmysharedlib.so,然后将主程序链接为:
gcc -o myprogram myprogram.c -lmysharedlib 

1
如果您使用CMake构建项目,可以使用以下命令:
TARGET_LINK_LIBRARIES(targetname libraryname)
例如:
TARGET_LINK_LIBRARIES(myprogram mylibrary)
要创建库"mylibrary",可以使用以下命令:
ADD_LIBRARY(targetname sourceslist)
例如:
ADD_LIBRARY(mylibrary ${mylibrary_SRCS})
此外,这种方法是跨平台的(而直接向gcc传递标志则不是)。

1
  • 共享库(.so)是存储了函数/类等实际源代码的目标文件(以二进制形式存储)。
  • 头文件(.h)是指明编译器可以在主要代码中需要时找到所需函数/类(在.so中)的引用文件。

因此,您需要同时使用它们两个。


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