使用GCC链接静态库

6

我正在阅读一本名为“GCC入门”的书,需要进行澄清。书中指出下面的代码会导致错误,但是当我编译时,它完美地构建和运行

#include <math.h>
#include <stdio.h>
int main (void) {
  double x = sqrt (2.0);
  printf ("The square root of 2.0 is %f\n", x);
  return 0;
}

我引用自书中的一段内容:“仅从这个源文件创建可执行文件会导致编译器在链接阶段报错:”

$ gcc -Wall calc.c -o calc
/tmp/ccbR6Ojm.o: In function `main':
/tmp/ccbR6Ojm.o(.text+0x19): undefined reference 
  to `sqrt'

该书提供的解决方案是,您应该按照以下方式包含数学库“libm.a”的路径:
$ gcc -Wall calc.c /usr/lib/libm.a -o calc

很不方便在程序中指定我们使用的内置库的路径。我能理解添加到自己自定义库的路径的原因,但是更现代的gcc版本有什么改变,以至于我们不需要包含libm.a的路径呢?虽然这本书已经相当老了(2004年出版),但是libm.a已经内置于gcc中了。 *更新* 我注意到taskinoor给出的答案展示了更新后的代码,如果sqrt()函数接收到的值在编译时未知,则需要使用-lm标志。
我使用VS学习C / C ++,但现在我的目标是学习和使用gcc。我拥有Visual Studio 2013,但VS编译器/链接器似乎并不那么挑剔。例如,我能够编译几乎任何简单的程序而无需指定神秘的编译器标志。
我正在使用附带于KUBUNTU 16.04.1的gcc版本5.4进行学习。

4
首先,尝试使用不同的书,因为几十年来,链接数学库的标准方法是使用“-lm”而不是完整路径到库。其次,https://gcc.gnu.org/releases.html 包含发布更改内容。 - John Griffin
1
@JohnGriffin:来自同一本书的内容:为了避免在命令行中指定长路径,编译器提供了一个名为“-l”的链接库快捷选项。例如,下面的命令: $ gcc -Wall calc.c -lm -o calc 等同于原始命令... - J. Allan
请注意,现在的默认值是链接动态库,而不是静态库。也许你想问的是与标准库链接吗? - Barmar
1
最近版本的gcc已经修复了这个bug(默认情况下无法链接libm)。 - M.M
@M.M,我正在使用gcc 5.4(Kubuntu 16.04.1),必须使用-lm标志。你在使用哪个版本的gcc?我是Linux的新手,尝试在Debian 8 Jessie上安装gcc 6,结果桌面GUI崩溃了。因为需要更新的gcc编译器,我选择了格式化并安装Kubuntu,但当然我希望我能使用gcc 6。 - Steven A
1
“-lm”是编译器选项之一。使用“-L”(大写字母'L')告诉编译器库的位置,例如/usr/lib。某些库,如“libc.so”,会自动包含;但“libm.so”不是其中之一。为了让链接器搜索该特定库,需要在链接语句末尾使用“-lm”(小写字母'L')参数。为什么“libm.so”变成了“m”?因为在引用库时,前导的“lib”和尾随的“.so”都被省略了。对于静态库,如“libm.a”,也有类似的考虑。 - user3629249
2个回答

6

sqrt (2.0);

现代GCC能够很好地确定您正在尝试找到常量的平方根,因此它能够在编译时计算出结果。您的目标代码不包含对sqrt的实际调用。

如果您使用通过scanf输入的变量,则在运行时它将无法链接到libm

int main() {
    double x;

    scanf("%lf", &x);
    printf("%lf\n", sqrt(x));

    return 0;
}

没有安装 libm 的情况下,Ubuntu 14.04 上的 gcc 4.8.4 会出现以下问题:

/tmp/ccVO2fRY.o: In function `main':
sqrt.c:(.text+0x2c): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

但如果我像你的例子那样使用一个常量而不是 x,那么它就可以正常链接而无需 libm

附注:我不知道 GCC 能够做到这一点的确切版本,希望其他人能指出。


我在 Linux 上也遇到了那个错误,但在 OS X 上没有任何错误。 - Barmar
@Barmar 在 OS X 上使用编译时无法解析的变量没有链接器错误?抱歉,我不了解 OS X。可能是其他问题。 - taskinoor
@taskinoor,感谢您的有益回答。如果没有-lm标志,我会得到几乎与您上面提到的相同的错误。我正在使用gcc 5.4,并将尝试升级,以查看是否有较新版本的gcc可以解决此问题。 - Steven A
@Barmar,我在VC 2013上使用taskinoor的代码片段时没有得到链接器错误,但是我必须在gcc 5.4中使用-lm标志。 - Steven A
因此,总体的解释是默认库集取决于实现。我认为 C 语言规范并不涵盖与系统库连接的内容,它仅指定包含声明的头文件。 - Barmar
@Barmar 我的观点是在某些编译器(例如 Linux 上的 gcc)上,如果可以在编译器时间解决常量值,就不需要链接库。但是,看起来这取决于具体实现。 - taskinoor

2

我注意到在某些操作系统上,常见的库可以在没有显式链接的情况下使用。特别是,我经常拿一个在我的Mac上开发的C项目,但在Linux上编译该项目时,除非我显式地链接使用的库(如libm),否则无法编译。

当然,这通常适用于动态链接而不是静态链接...


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