运行示例 C 代码是一项痛苦的工作,除非它附带 makefile。
我经常发现自己有一个 C 文件,其中包含的代码据说可以做一些非常酷的事情,但是第一次基本的编译尝试 ( gcc main.c
) 失败了——
main.c:(.text+0x1f): undefined reference to `XListInputDevices'
clang-3.7: error: linker command failed with exit code 1 (use -v to see invocation)
——或类似的。
我知道这意味着我缺少正确的链接器标志,例如-lX11
,-lXext
或-lpthread
。
但哪些呢?
我目前处理这个问题的方法是查找包含函数的库头,使用 Github 的搜索来查找导入相同标头的其他程序,打开其 makefile,找到链接器标志,将它们复制到我的编译命令中,然后继续删除标志,直到找到仍然可以编译的最小集。
这是低效、无聊的,让我觉得一定有更好的方法。
答案1
问题是如何通过检查源文件来确定要使用的链接器标志。下面的示例适用于 Debian。头文件是这里需要注意的相关项目。
因此,假设有一个包含头文件的 C 源文件
#include <X11/extensions/XInput.h>.
我们可以使用 进行搜索XInput.h
,例如apt-file
.如果您知道这个头文件包含在已安装的包中,dpkg -S
或者dlocate
也将起作用。例如
apt-file search XInput.h
libxi-dev: /usr/include/X11/extensions/XInput.h
这告诉您该头文件属于 libxi 的开发包(对于 C 库,开发包(通常采用libname-dev
或的形式libname-devel
)包含头文件),因此您应该使用-lxi
链接器标志。
类似的方法应该适用于任何具有包管理系统的发行版。
答案2
问题的核心:头文件与库名称
问题的核心已发布在问题本身下方的评论:
对于 XListInputDevices,适当的包含行是
#include <X11/extensions/XInput.h>
。我在想我如何根据 -lSomething 标志判断我需要什么
-lXi
为了正确回答这个问题,我们需要理解(或者更确切地说libXi.so
)和之间的区别XInput.h
。这XInput.h
是一个头文件并-l
查找目标文件。根据有关 gcc 使用的 Red Hat 文档,第16.1节,
库使用特殊的文件名约定:名为 foo 的库预计以文件 libfoo.so 或 libfoo.a 的形式存在。 GCC 的链接输入选项会自动理解此约定,但输出选项不会自动理解此约定
标头和库都是必需的,并且两者相关,但不相同(参考):
...如果您#include 标头,编译器不会将您调用的函数的代码插入program.o 中。它只是插入对它们的引用。
如前所述,标头和链接器有些不同,因为标头存储函数和其他内容的声明(或外行术语 - 描述),而库 - 存储实际对象(参考)或这些函数的编译版本。正如这个答案所说:
- 标题是您可以拨打的电话号码,而...
- ...图书馆是您可以到达的实际人!
标头和库如何一起发挥作用?它们都是构建应用程序的必要步骤。首先,收集功能和接口的描述(又名编译的)然后你写的代码是链接的到实现实际接口和功能的库。
图书馆本身应该使用包含的标头进行编译(参考)进入图书馆。事实上你可以看一个例子如何创建简单的静态库。
所以你的问题的答案如下:标头和库名称之间没有保证关系。(或者至少到目前为止我还没有找到;另请参阅这个参考文献)。而且,找出如何匹配它们就是了解包或源代码中的内容的问题。
事实上你可以这样做来证明这一点:
$ echo '#include<errno.h> int main(){return 0;}' | gcc -S -lc
这表明我们正在针对 libc 进行编译,但作为 libc 一部分的标头名称不同。
证明这一点的另一个例子是#include <math.h>
,它是 的一部分libc
,但实际实现是在 中libm.so.6
,因此您将看到用 编译的代码gcc -lm
。
$ apt-file find /lib/x86_64-linux-gnu/libm.so.6
libc6: /lib/x86_64-linux-gnu/libm.so.6
查找相关的.so或.a库文件
很多谜团可以通过文档来解决。我们参考一下gcc
链接选项的文档(注意,粗体强调的是我的):
-图书馆
-l 库
链接时搜索名为library的库。 (将库作为单独参数的第二种选择仅用于 POSIX 合规性,不推荐。)
-l 选项是由 GCC 直接传递给链接器。有关确切的详细信息,请参阅链接器文档。下面的一般描述适用于 GNU 链接器。
好的,所以它gcc
本身实际上并不是在查找库。这是链接器的工作。但让我们继续读下去。
链接器搜索标准目录列表对于图书馆。搜索的目录包括几个标准系统目录以及您使用 -L 指定的任何目录。
因此,有一个标准的目录列表。太好了,范围缩小了。我们如何获取该列表?单程 其中之一是使用,ldconfig -v
用户慷慨地解释了这一点特洛托里姆
但gcc
文档进一步解释:
静态库是目标文件的档案,文件名类似于 liblibrary.a。某些目标还支持共享库,其名称通常类似于 liblibrary.so。如果同时找到静态库和共享库,则链接器会优先链接共享库,除非使用 -static 选项。
在命令中编写此选项的位置会有所不同;链接器按照指定的顺序搜索和处理库和目标文件。因此,“foo.o -lz bar.o”在文件 foo.o 之后但在 bar.o 之前搜索库“z”。如果 bar.o 引用“z”中的函数,则可能不会加载这些函数。
换句话说,您真正指示gcc
并随后ld
要做的事情是查找文件libxi.so
(因此成为共享对象扩展)或libxi.a
在标准路径列表中。如果说我们存储了一些东西/opt
而ld
链接器不知道它,那么将任何东西传递给-l
将不起作用。
接受的答案似乎暗示了这一点,凭借与该对应的库的大多数 debian 包名称libwhatever.so
名称相对应的库的大多数 debian 包名称。但我无法确认或否认这一点,因为我对 Debian 打包的了解足够多,只是为了满足我的卑微需求,而不是这些微小的细节。我所知道的是apt 可能会触发ld
缓存重建。
现在,同样重要的是要意识到,ld
实际上所有这些目录都在缓存中/etc/ld.so.cache
。因此,如果从缓存中删除目录,链接器将不知道库的存在。
当然,如果我们想基于 的例子来测试上述所有理论libxi-devel
,我们可以这样做:
$ dpkg-query -L libxi-dev | grep -i '.so'
/usr/share/man/man3/XIDefineCursor.3.gz
/usr/lib/x86_64-linux-gnu/libXi.so
/usr/share/man/man3/XIUndefineCursor.3.gz
然后检查ldconfig
确认是否/usr/lib/x86_64-linux-gnu
在搜索目录列表中...它是:
$ ldconfig -p | grep libXi.so
libXi.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libXi.so.6
libXi.so.6 (libc6) => /usr/lib/i386-linux-gnu/libXi.so.6
libXi.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libXi.so
现在这也提出了一个问题,这个.h
文件没用吗?不,显然gcc
仍然需要它在编译时存在(因为gcc
确实检查了包含路径并且因为有些库只能是头文件)
$ mv /usr/include/X11/extensions/XInput.h /tmp
$ echo '#include <X11/extensions/XInput.h> void main(){return 0;}' | gcc -lXi -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "<stdin>"
<stdin>:1:36: warning: extra tokens at end of #include directive
<stdin>:1:10: fatal error: X11/extensions/XInput.h: No such file or directory
compilation terminated.
答案3
这是一个社区 wiki,用于收集其他发行版的等效工具法希姆的方法。请随意编辑,但请保持按字母顺序排序以便搜索。
拱
pkgfile
从存储库中使用extra
,将头文件名作为参数传递。
例子:
$ pkgfile XInput.h
extra/libxi
extra/nx-headers
德班
(和任何基于 Debian 的东西使用dpkg
)
apt-file search
对于头文件名,如所涵盖的。
根图
正如 warl0ck 的另一个问题所述,使用pfl
包的e-file
程序,或者,基于网络的 Portage 文件列表搜索。