将代码与`floor();`,`ceil();`和`pow();`联系起来时出错

时间:2016-08-20 16:54:08

标签: c linker-errors flags undefined-reference math.h

我在GNU / Linux Debian 8.5下编码

我有一个简单的程序。

如果我用gcc prog.c编译它就可以了!

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

int main(int argc, char const *argv[]) {

    float _f = 3.1415f;

    floor(_f);
    ceil(_f);

    return 0;
}

如果我添加pow(),就会发芽,它说找不到pow,我需要添加gcc prog.c -lm才能使其正确。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

int main(int argc, char const *argv[]) {

    float _f = 3.1415f;

    floor(_f);
    ceil(_f);
    pow(_f, 2);

    return 0;
}

如果我是对的,pow()ceil()floor()都来自<math.h>

那么为什么不要floor()ceil()抛出编译错误,而pow()会抛出-lm标记?

3 个答案:

答案 0 :(得分:4)

从技术上讲,所有这些都需要-lm才能正常工作。他们所有的man页都包含以下内容:

  

与-lm链接。

但是你的不是编译器错误而是链接器错误,那就是你的程序编译得很好,但是如果你不使用-lm就链接它就无法找到pow()的实现但它实际上找到了ceil()的实现。

这可能是因为在您的体系结构/配置中ceil()是内联函数或内部函数,可能有一个简单的CPU指令来执行它,因此不需要库。但pow()并非如此,您需要关联libm

更新:我刚刚做了一些实验,而-O0 -lm所有功能都需要-O2pow()只需要/usr/include/bits/mathinline.h 。修改我发现文件ceil()的内联实现为floor()和{{1}} ...

答案 1 :(得分:3)

编译器只抱怨pow()而不是floor()ceil(),因为它会生成floor()ceil()的内联代码以及{{}的外部调用{1}}在链接时无法解析,因为您忘记了命令行上的pow()库:m代表-lm 的链接。

顺便说一句,由于您不存储这些函数的返回值,因此编译器可能会使用其对这些纯函数的内在知识(在libm.a中以某种方式传达)来完全删除调用。它可以为<math.h>ceil()而不是floor()执行此操作,这也可以解释观察到的行为。

实际上,可以在http://gcc.goldbolt.org/#上验证,没有命令行选项,您的代码编译为:

pow()

出于某种原因,编译器仅为main: pushq %rbp movq %rsp, %rbp subq $32, %rsp movl %edi, -20(%rbp) movq %rsi, -32(%rbp) movss .LC0(%rip), %xmm0 movss %xmm0, -4(%rbp) cvtss2sd -4(%rbp), %xmm0 movsd .LC1(%rip), %xmm1 call pow movl $0, %eax leave ret .LC0: .long 1078529622 .LC1: .long 0 .long 1073741824 floor生成内联代码,如所观察到的那样。

使用ceil时会删除所有内容:

-O2

如果修改代码以将值存储到全局变量中,则会生成对main: xorl %eax, %eax ret floor()ceil()的库调用而不进行优化,如果您编译,则由编译器计算值使用pow()进行优化。

答案 2 :(得分:1)

您得到的错误是链接错误,而不是编译错误 Floor和ceil可能位于其他库中,通常编译器不需要诊断缺少的标头或库。